AC自动机学习笔记
这里给三个板子做一个总结
简单版
AC自动机就是KMP+Trie,fail指针在多模式串之间跳跃来进行匹配。
fail指针有一种很妙的写法:
inline void bfs(){
register int i,tot;
for(i=0;i<26;i++)if(f[0].son[i]) q.push(f[0].son[i]);
while(!q.empty()){
now=q.front();q.pop();
for(i=0;i<=25;i++){
tot=f[now].son[i];
if(f[now].son[i]) f[tot].fail=f[f[now].fail].son[i],q.push(tot);
else f[now].son[i]=f[f[now].fail].son[i];
}
}
}
把一棵树补成一张图,拿来更好地匹配。
代码实现:
#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
using namespace std;
int n,m,k,x,y,z,cnt,ans,now,nows;
char s[1000039],a[1000039];
struct tree{int son[27],cnt,fail;}f[1000039];
inline void get(){
m=strlen(a+1);
register int i;now=0;
for(i=1;i<=m;i++){
if(!f[now].son[a[i]-'a'])f[now].son[a[i]-'a']=++cnt;
now=f[now].son[a[i]-'a'];
}
f[now].cnt++;
}
queue<int> q;
inline void bfs(){
register int i;
for(i=0;i<=25;i++){
now=f[0].son[i];
if(now) q.push(now);
}
while(!q.empty()){
nows=q.front();
q.pop();
for(i=0;i<=25;i++){
now=f[nows].son[i];
if(now){
f[now].fail=f[f[nows].fail].son[i];
q.push(now);
}
else f[nows].son[i]=f[f[nows].fail].son[i];
}
}
}
inline int check(){
register int i;
n=strlen(s+1);
now=0;
for(i=1;i<=n;i++){
nows=f[now].son[s[i]-'a'];
if(nows&&~f[nows].cnt){
ans+=f[nows].cnt;
f[nows].cnt=-1;
nows=f[nows].fail;
}
now=f[now].son[s[i]-'a'];
}
return ans;
}
int main(){
// freopen("1.in","r",stdin);
register int i;
scanf("%d",&n);
for(i=1;i<=n;i++) cin>>a+1,get();
bfs();
cin>>s+1;
printf("%d\n",check());
}
加强版
这个可以匹配到以后暴力跳fail指针,给沿线每个指针加上\(1\)
这样的复杂度是\(O(70|S|+\sum\limits{|T|})\)
然而这个\(70\)可以消掉,注意我们暴力跳了很多无用的路径,所以可以把这些放到最后弄。一遍拓扑就好了。时间复杂度\(O(|S|+\sum\limits{|T|})\)常数略大。
其实二次加强版是一样的,就不赘述了。
代码实现:
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int n,m,k,x,y,z,ans[200039],t,cnt,id[200039],in[200039],pus,now;
struct AC{int tot,cnt,son[26],fail;}f[200039];
char s[200039],a[2000039];
inline void get(int x){
register int i,now=0;
for(i=1;i<=m;i++)now=f[now].son[s[i]-'a']?f[now].son[s[i]-'a']:(f[now].son[s[i]-'a']=++cnt);
id[x]=now;f[now].cnt++;
}
queue<int> q;
inline void bfs(){
register int i,tot;
for(i=0;i<26;i++)if(f[0].son[i]) q.push(f[0].son[i]);
while(!q.empty()){
now=q.front();q.pop();
for(i=0;i<=25;i++){
tot=f[now].son[i];
if(f[now].son[i]) f[tot].fail=f[f[now].fail].son[i],q.push(tot);
else f[now].son[i]=f[f[now].fail].son[i];
}
}
}
inline void find(){
register int i,now=0;
for(i=1;i<=m;i++)
now=f[now].son[a[i]-'a'],f[now].tot++;
}
int main(){
freopen("1.in","r",stdin);
register int i;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%s",s+1);
m=strlen(s+1);get(i);
}bfs();
scanf("%s",a+1);m=strlen(a+1);find();
for(i=1;i<=cnt;i++)in[f[i].fail]++,ans[i]=f[i].tot;
for(i=1;i<=cnt;i++)if(!in[i]) q.push(i);
while(!q.empty()){
now=q.front();q.pop();
in[f[now].fail]--;ans[f[now].fail]+=ans[now];
if(!in[f[now].fail]) q.push(f[now].fail);
}
for(i=1;i<=n;i++) printf("%d\n",ans[id[i]]);
}