首页 > 代码库 > 【bzoj3172】: [Tjoi2013]单词 字符串-AC自动机

【bzoj3172】: [Tjoi2013]单词 字符串-AC自动机

【bzoj3172】: [Tjoi2013]单词

先用所有单词构造一个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 }
View Code

一开始zz把strlen放到循环里慢了十几倍

【bzoj3172】: [Tjoi2013]单词 字符串-AC自动机