【bzoj3172】: [Tjoi2013]单词 字符串-AC自动机
先用所有单词构造一个AC自动机
题目要求的是每个单词在这个AC自动机里匹配到的次数
每次insert一个单词的时候把路径上的cnt++
那么点p->cnt就是以root到p这条路径为前缀的单词的个数
如果p->fail指向了点q,那么就会对q点产生p->cnt的贡献(root到q一定为root到p的后缀)
最后递推统计完所有fail的贡献,找到关键点输出就可以了
1 /* http://www.cnblogs.com/karl07/ */ 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <cmath> 6 #include <algorithm> 7 using namespace std; 8 9 struct trie{ 10 trie *next[26],*fail; 11 int cnt,x; 12 }t[1000005]; 13 14 int n; 15 char s[1000005]; 16 trie *root,*NEW=t; 17 trie *Q[1000005],*wh[300]; 18 19 trie *new1(int x){NEW++; NEW->cnt=0; NEW->x=x; return NEW;} 20 21 trie *insert(trie *p,int x){ 22 if (!p->next[x]) p->next[x]=new1(x); 23 p->next[x]->cnt++; 24 return p->next[x]; 25 } 26 27 #define pnf p->next[i]->fail 28 void build_fail(){ 29 int l=0,r=0; 30 for (int i=0;i<26;i++) if (root->next[i]) { Q[++r]=root->next[i]; root->next[i]->fail=root;} 31 while (l!=r){ 32 trie *p=Q[++l]; 33 for (int i=0;i<26;i++){ 34 if (p->next[i]){ 35 Q[++r]=p->next[i]; 36 for (pnf=p->fail ; pnf!=root && !pnf->next[i] ; pnf=pnf->fail); 37 if (pnf->next[i]) pnf=pnf->next[i]; 38 } 39 } 40 } 41 for (int i=r;i>=1;i--) Q[i]->fail->cnt+=Q[i]->cnt; 42 } 43 44 int main(){ 45 scanf("%d",&n); 46 root=new1(-1); 47 for (int i=1;i<=n;i++){ 48 scanf("%s",s); 49 wh[i]=root; 50 int l=strlen(s); 51 for (int j=0;j<l;j++) wh[i]=insert(wh[i],s[j]-'a'); 52 } 53 build_fail(); 54 for (int i=1;i<=n;i++) printf("%d\n",wh[i]->cnt); 55 return 0; 56 }
一开始zz把strlen放到循环里慢了十几倍