认为上面这篇blog已经讲述的十分清楚(一定要画图多手动模拟,,才能感受AC自动机的奥妙),所以解释两个我想了很久也没想明白的问题 (是基于代码的)
1.为什么要用BFS太蠢了QWQ
2.连接fail时的一些特殊情况到底是怎么处理的
例如当前有一颗这样的树:
我们已经按照操作,搜索到了标红节点,按照AC自动机的思想连fail边,因为我们已经处理过它的父亲节点c节点的fail值,所以d节点的fail值应该等于。。讲不清了大概是这样连边:
然而虚拟d点的编号为0,那这样红色d点岂不是相当于连到了顶点,但其实不是,我们也可以看出来d点应该连在第3列的d点上
但这是怎么处理的呢?因为是BFS,是按照深度由浅到深进行处理的,所以虚拟点其实已经处理过且值已变成另一个d点了
就像是这样:
推荐题目:洛谷P3808
代码如下:
#include<bits/stdc++.h> using namespace std; int num,ans; struct{ int vis[28]; int fail; int end; }a[1000006]; void build(string s){ int i,l,now=0; l=s.length(); for(i=0;i<l;i++){ if(a[now].vis[s[i]-'a']==0){ num++; a[now].vis[s[i]-'a']=num; } now=a[now].vis[s[i]-'a']; } a[now].end++; } void fail(){ queue<int>q; int now,i; for(int i=0;i<26;++i){ if(a[0].vis[i]!=0) q.push(a[0].vis[i]); }//一定要有,若只是将0放入队列中,那第一层字母将会指向它自己,然而它应该指向0点 while(!q.empty()){ now=q.front();q.pop(); for(i=0;i<26;i++){ if(a[now].vis[i]){ a[a[now].vis[i]].fail=a[a[now].fail].vis[i]; q.push(a[now].vis[i]); } else a[now].vis[i]=a[a[now].fail].vis[i]; } } } void find(string s){ int i,l,now,j; l=s.length(); now=0; for(i=0;i<l;i++){ now=a[now].vis[s[i]-'a']; for(j=now;j&&a[j].end!=-1;j=a[j].fail){ ans+=a[j].end; a[j].end=-1; } } } int main(){ int i,j,l,n; string s; cin>>n; for(i=1;i<=n;i++){ cin>>s; build(s); } fail(); //for(i=1;i<=num;i++)printf("%d %d ",a[i].fail,a[i].end); cin>>s; find(s); printf("%d",ans); }