bzoj3172: [Tjoi2013]单词(AC自动机)

3172: [Tjoi2013]单词

题目:传送门

题解:

   其实这题还是蛮裸的一道AC

   关键点在于对失败指针的运用:

   把所有的失败指针连在一起其实可以构成一棵树

   对于节点i对的fail指向的j,root~j一定在root~i这一段中出现过

   那么我们就用小段更新大段,一开始就统计所有以当前节点结尾的串出现的次数

   具体看代码

AC代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 struct Tire
 8 {
 9     int c[30],s,fail;
10     Tire()
11     {
12         s=fail=0;
13         memset(c,-1,sizeof(c));
14     }
15 }tr[511000];
16 int trlen,ans,n,ed[210];
17 char a[1110000];
18 void bt(int id,int root)
19 {
20     int x=root,len=strlen(a+1);
21     for(int i=1;i<=len;i++)
22     {
23         int y=a[i]-'a'+1;
24         if(tr[x].c[y]==-1)tr[x].c[y]=++trlen;
25         x=tr[x].c[y];
26         tr[x].s++;
27     }
28     ed[id]=x;    
29 }
30 int list[1110000];
31 void bfs()
32 {
33     int head=1,tail=1;list[1]=0;
34     while(head<=tail)
35     {
36         int x=list[head];
37         for(int i=1;i<=26;i++)
38         {
39             int son=tr[x].c[i];
40             if(son==-1)continue;
41             if(x==0)tr[son].fail=0;
42             else
43             {
44                 int j=tr[x].fail;
45                 while(j!=0 && tr[j].c[i]==-1)j=tr[j].fail;
46                 tr[son].fail=max(tr[j].c[i],0);
47             }
48             list[++tail]=son;
49         }
50         head++;
51     }
52     for(int i=tail;i;i--)tr[tr[list[i]].fail].s+=tr[list[i]].s;//小段更新大段 
53 }
54 
55 int main()
56 {
57     scanf("%d",&n);trlen=0;
58     for(int i=1;i<=n;i++)
59     {
60         scanf("%s",a+1);
61         bt(i,0);
62     }
63     bfs();
64     for(int i=1;i<=n;i++)printf("%d
",tr[ed[i]].s);
65     return 0;
66 }
原文地址:https://www.cnblogs.com/CHerish_OI/p/8342366.html