您的位置:首页 > 理论基础 > 数据结构算法

AC自动机模板(hdu2222)

2015-02-03 17:20 344 查看
刚刚学习了AC 自动机,先记录一个数组写法的模板。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define Max 26
#define N 1000005

struct node
{
int next[Max];//每一个节点可以扩展到的字母
int fail;//每一个节点的失配指针
int count;
void init()//构造
{
memset(next,-1,sizeof(next));
fail=0;
count=0;
}
}s
;
int num;//记录节点编号
char str[55];//模板串,单词
char ss
;//母串
int q
,tail,head;//队列相关信息
void cas_init()//在整个程序前构造root
{
s[0].init();//初始化头结点
num=1;//当前的节点数量为1
}
void insert()
{
int len=strlen(str);
int p=0,i,j;
for(i=0;i<len;i++)
{
j=str[i]-'a';//求出字母在next中的编号
if(s[p].next[j]==-1)//如果为空则构造新的,否则顺着上次的开始往下构造
{
s[num].init();//初始化当前节点
s[p].next[j]=num++;//连向当前节点,并是num++来扩充节点
}
p=s[p].next[j];//向下遍历
}
s[p].count++;//增加离根节点这条路径
}
void build_ac()
{
tail=head=0;//初始化队列
int temp,p;
for(int i=0;i<Max;i++)
{
if(s[0].next[i]!=-1)
{
q[tail++]=s[0].next[i];//和根节点相连的都入队
}
}
while(tail!=head)
{
p=q[head++];//记录队首节点
for(int i=0;i<Max;i++)//遍历首节点的next
{
if(s[p].next[i]!=-1)//如果节点next不为空
{
q[tail++]=s[p].next[i];//将儿子节点入队列
temp=s[p].fail;//记录节点的失配指针指向
while(temp>0&&s[temp].next[i]==-1)//当失配指针不为root时一直循环找到一的儿子节点不为空或到了root
temp=s[temp].fail;
if(s[temp].next[i]!=-1)//如果当前节点有儿子的话记录下来备用
temp=s[temp].next[i];
s[s[p].next[i]].fail=temp;//是当前节点的失配指针指向刚才记录的节点完成失配指针的构造
}
}
}
}
void query()
{
int len=strlen(ss);
int p=0;
int temp=0;
int ans=0;
for(int i=0;i<len;i++)
{
int id=ss[i]-'a';
while(p>0&&s[p].next[id]==-1)//当前p指针不是root和找不到儿子时,一直找下去(及kmp的while)
p=s[p].fail;//一直寻找失配指针
if(s[p].next[id]!=-1)//找到适合的失配指针
{
p=s[p].next[id];//指向这个儿子节点,更新p的值进行下次匹配
temp=p;
while(temp>0&&s[temp].count!=-1)//temp > 0表示还没到root,count != -1表示指针前还有单词
{
ans+=s[temp].count;//加上有的单词个数
s[temp].count=-1;//不重复计算
temp=s[temp].fail;//一直寻找失配指针
}
}
}
printf("%d\n",ans);
}
int main()
{
int n;
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
cas_init();
for(int i=1;i<=n;i++)
{
scanf("%s",str);
insert();
}
build_ac();
scanf("%s",ss);
query();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构