bzoj 3172 AC自动机

      初学AC自动机,要先对于每一个模式串求出来trie树,在此基础上构建fail指针,然后在trie树加上失配边构建出整张trie图。

      AC自动机的原理和KMP差不多,一个节点的fail指针就是指向trie树上一个最长前缀等于这个单词的后缀。

      首先fail[0]=0,然后把0有的每个孩子push进一个队列里(如果直接push(0)的话会出现f[x]=x),然后bfs。

      设一个节点为u,父亲为v,那么如果ch[fail[v]][u是v的哪个孩子]!=0,那么fail[u]就等于它,否则就不停的跳fail直到0或存在这个孩子。

      但我们可以用trie图来优化这个过程,每个节点的每个孩子都不为空,如果原本为空就直接指向上一步用while循环求的那个孩子(具体看代码),于是跳的时候无脑跳一步就行了。

      考虑这道题,一个单词一定为trie上的一个节点,而且它出现在文章中一定是一个前缀的后缀,所以那些fail指针直接或间接指向它的节点一定包含这个前缀,所以用bfs序倒着dp一遍就行了。

   

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 #define N 1000005
 7 using namespace std;
 8 int n;
 9 char s[N];
10 int cnt;
11 int ch[N][26],f[N],ans[N],sum[N];
12 int b[N];
13 void in(int xx)
14 {
15     int now=0,l=strlen(s);
16     for(int i=0;i<l;i++)
17     {
18         int x=s[i]-'a';
19         if(!ch[now][x])ch[now][x]=++cnt;
20         now=ch[now][x];sum[now]++;
21     }
22     b[xx]=now;
23 }
24 queue<int>q;int a[N];int tot;
25 void fail()
26 {
27     f[0]=0;
28     for(int i=0;i<26;i++)
29     {
30         int u=ch[0][i];
31         if(u)q.push(u),a[++tot]=u;
32     }
33     while(!q.empty())
34     {
35         int tmp=q.front();q.pop();a[++tot]=tmp;
36         for(int i=0;i<26;i++)
37         {
38             int u=ch[tmp][i];
39             if(!u)
40             {
41                 ch[tmp][i]=ch[f[tmp]][i];continue;
42             }
43             q.push(u);f[u]=ch[f[tmp]][i];
44         }
45     }
46 }
47 int main()
48 {
49     scanf("%d",&n);
50     for(int i=1;i<=n;i++)
51     {
52         scanf("%s",s);
53         in(i);
54     }
55     fail();
56     for(int i=tot;i>=1;i--)sum[f[a[i]]]+=sum[a[i]];
57     for(int i=1;i<=n;i++)
58     {
59         printf("%d\n",sum[b[i]]);
60      } 
61     return 0;
62 }

 

posted @ 2016-12-09 09:54  SD_le  阅读(179)  评论(0编辑  收藏  举报
重置按钮