AC自动机的一个常见的例子就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过。
这题一定要注意建树时为128叉树:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
char number[1000005];
struct Trie
{
int Next[100010][128],Fail[100010],End[100010];
int root,L;
int newnode()
{
for(int i=0;i<128;i++)
{
Next[L][i]=-1;
}
End[L++]=0;
return L-1;
}
void init()
{
L=0;
root=newnode();
}
void Insert(char s[],int n) //插入单词
{
int len,i,now;
len=strlen(s);
now=root;
for(i=0;i<len;i++)
{
if(Next[now][s[i]]==-1)
{
Next[now][s[i]]=newnode();
}
now=Next[now][s[i]];
}
End[now]=n;
}
void build() //构造Fail指针
{
int i;
queue<int>Q;
Fail[root]=root;
for(i=0;i<128;i++)
{
if(Next[root][i]==-1)
Next[root][i]=root;
else
{
Fail[Next[root][i]]=root;
Q.push(Next[root][i]);
}
}
while(!Q.empty())
{
int now=Q.front();
Q.pop();
for(int i=0;i<128;i++)
{
if(Next[now][i]==-1)
Next[now][i]=Next[Fail[now]][i];
else
{
Fail[Next[now][i]]=Next[Fail[now]][i];
Q.push(Next[now][i]);
}
}
}
}
bool Query(int T,int id) //询问是否出现病毒,且将出现的病毒的序号输出
{
bool flag=false;
bool used[600];
memset(used,false,sizeof(used));
int len=strlen(number);
int now=root,i;
int temp;
for(i=0;i<len;i++)
{
now=Next[now][number[i]];
temp=now;
while(temp!=root)
{
if(End[temp]!=0)
{
used[End[temp]]=true;
flag=true;
}
temp=Fail[temp];
}
}
if(!flag)return false;
printf("web %d: ",id);
i=1;
while(!used[i])i++;
printf("%d",i);
for(i=i+1;i<=T;i++)
{
if(used[i])printf(" %d",i);
}
printf("\n");
return true;
}
};
Trie ac;
int main()
{
int T,i,n,sum,sum2;
char buf[500010];
while(~scanf("%d",&T))
{
sum2=0;
ac.init(); //初始化
for(i=1;i<=T;i++)
{
scanf("%s",buf); //读取单词
ac.Insert(buf,i); //将单词存入树中
}
ac.build(); //建立Fail指针
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%s",number); //n个网站,每个都要进行查询
if(ac.Query(T,i))
{
sum2++; //统计带病毒的网站数
}
}
printf("total: %d\n",sum2);
}
return 0;
}