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\n",tr[ed[i]].s); 65 return 0; 66 }