1.参考资料:
http://blog.csdn.net/mobius_strip/article/details/22549517
http://www.cppblog.com/mythit/archive/2009/04/21/80633.html
http://blog.csdn.net/niushuai666/article/details/7002823
http://hzwer.com/2170.html
2.什么是AC自动机?
AC自动机并不意味这你学会了就能自动AC了......
AC自动机就是在trie树上写kmp.....
那么怎么写呢?尤其是怎么确定next数组指向的值呢?
如图(图是网上找的)
我们构造一棵trie树,然后对于每一个点,顺着其父亲节点的next指针往上走,直到找到一个节点,它有一个子节点和该节点的字符相同(其实应该字符在边上,不过放到点上也没关系啦),那么该节点的next指针就指向这个子节点。
在我们匹配字符串的时候,next指针可以快速帮助匹配,方式和kmp很像,寻找下一个字符路径的时候也是这么顺着next指针往上,然后找子节点。
这么说可能还有点迷糊,看看代码就明白了。
我之前写AC自动机总是要130行+,但是自从看了hzwer的AC自动机,我觉得整个世界都美好起来了。
3.代码(其实我是学的hzwer的)
题目是HDU2222,模板题。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iomanip>
#include<cmath>
#include<algorithm>
using namespace std;
int T,sz,ans,n;
char s[51],ss[1000001];
int a[500001][27],q[500001],fa[500001],danger[500001];
bool mak[500001];
void tri(){//构建trie树
int from=1,i,j,sum,l=strlen(s);
for(i=0;i<l;i++){
sum=s[i]-'a'+1;
if(a[from][sum]){from=a[from][sum];}
else {
sz++;a[from][sum]=sz;from=sz;fa[sz]=mak[sz]=danger[sz]=0;
for(j=1;j<=26;j++)a[sz][j]=0;
}
}
danger[from]++;
}
void ac(){//寻找失配指针next
int from,i,j,head=1,tail=1;
q[1]=1;fa[1]=0;
while(head<=tail){
from=q[head];
for(i=1;i<=26;i++){
if(!a[from][i])continue;
j=fa[from];
while(!a[j][i])j=fa[j];
fa[a[from][i]]=a[j][i];//根节点从1开始,那么0是摆看的,很巧妙的处理。
tail++;q[tail]=a[from][i];
}
head++;
}
}
void solve(){
int i,j,from=1,sum,l=strlen(ss);
for(i=0;i<l;i++){
mak[from]=1;
sum=ss[i]-'a'+1;
while(!a[from][sum])from=fa[from];
from=a[from][sum];
if(!mak[from])//顺着失配边网上寻找危险节点
for(j=from;j;j=fa[j]){
ans+=danger[j];danger[j]=0;
}
}
printf("%d\n",ans);
}
int main()
{
int i,j;
scanf("%d",&T);
while(T--){
sz=1;ans=0;mak[1]=fa[1]=danger[1]=0;
scanf("%d",&n);
for(i=1;i<=26;i++)a[0][i]=1,a[1][i]=0;
for(i=1;i<=n;i++){
scanf("%s",s);tri();
}
ac();
scanf("%s",ss);
solve();
}
return 0;
}