BZOJ3172 TJOI2013 单词 AC自动机
题意:给定N个字符串,设S为N个字符串首尾相连组成的字符串,查询每个字符串在S中出现的次数。
题解:
首先我们在构造Trie的时候,将构造过程中经过的节点的cnt全部++,此时cnt中记录该模式串被多少个模式串包含。
如果A为B的子串,B为C的子串,显然A也是C的子串,因此我们从下往上回溯,每回溯到一个节点都x->fail->cnt+=x->cnt
#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; const int MAXK=26; const int MAXN=200+2; const int MAXL=1000000+2; struct NODE{ int cnt; NODE *child[MAXK],*fail; }*root,*mark[MAXL],*q[MAXL]; int N; char S[MAXL]; NODE *NewNode(){ NODE *x=new NODE(); memset(x,0,sizeof(NODE)); return x; } void Insert(char *S,NODE *&x,int k){ if(!x) x=NewNode(); x->cnt++; if(!*S){ mark[k]=x; return; } Insert(S+1,x->child[(*S)-'a'],k); } void Get_Fail(NODE *x){ int l=0,r=0; for(int i=0;i<MAXK;i++) if(x->child[i]) x->child[i]->fail=root,q[++r]=x->child[i]; NODE *p,*t; while(l<r){ t=q[++l]; for(int i=0;i<MAXK;i++) if(t->child[i]){ p=t->fail; while(p!=root && !p->child[i]) p=p->fail; if(p->child[i]) p=p->child[i]; t->child[i]->fail=p; q[++r]=t->child[i]; } else t->child[i]=t->fail->child[i]; } for(int i=r;i;i--) q[i]->fail->cnt+=q[i]->cnt; } int main(){ root=NewNode(); scanf("%d",&N); for(int i=1;i<=N;i++){ scanf("%s",S); Insert(S,root,i); } Get_Fail(root); for(int i=1;i<=N;i++) printf("%d\n",mark[i]->cnt); return 0; }