题目:http://acm.hdu.edu.cn/showproblem.php?pid=2222
第一道AC自动机!
T了无数边后终于知道原来它是把若干询问串建一个自动机,把模式串放在上面跑;而且只走模式串的前缀,用 fail 指针来精准遍历每个前缀的每个后缀,就能行了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e6+5,K=27; int T,n,c[N][K],fail[N],ed[N],len,ans,tot; int q[N],he,tl; char ch[N]; void init() { tot=1;ans=he=tl=0; memset(c,0,sizeof c);memset(fail,0,sizeof fail); memset(ed,0,sizeof ed); } void insert() { int nw=1; for(int i=1,d;i<=len;i++) { d=ch[i]-'a'; if(!c[nw][d]) c[nw][d]=++tot; nw=c[nw][d]; } ed[nw]++; } void gtfl() { for(int i=0;i<26;i++) if(c[1][i]) { q[++tl]=c[1][i]; fail[c[1][i]]=1; } while(he<tl)//< { int k=q[++he]; for(int i=0;i<26;i++) if(c[k][i]) { q[++tl]=c[k][i]; int nw=fail[k]; while(!c[nw][i]&&nw!=1) nw=fail[nw]; if(!c[nw][i]) fail[c[k][i]]=1; else fail[c[k][i]]=c[nw][i]; } } } void solve() { int nw=1; for(int i=1,d,cr;i<=len;i++)//只走一遍前缀 { d=ch[i]-'a'; while(!c[nw][d]&&nw!=1) nw=fail[nw]; nw=c[nw][d]; if(!nw) nw=1; cr=nw; while(cr!=1&&ed[cr])//遍历该前缀的每个后缀 { ans+=ed[cr];ed[cr]=0;cr=fail[cr]; } } } int main() { scanf("%d",&T); while(T--) { init(); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",ch+1); len=strlen(ch+1); insert(); } gtfl(); scanf("%s",ch+1); len=strlen(ch+1); solve(); printf("%d\n",ans); } return 0; }