AC自动机

KMP+Trie树(AC自动机将两者优点结合起来)

三者对比下:
KMP:处理1个模式串和1个文本串匹配问题(引入了一个kmp数组)
Trie树:查询n个前缀或者单词在一个字典【n个单词所组成的】里的出现问题 (引入一个特别的Trie树)
AC自动机:给定n个模式串和1个文本串,查询模式串在文本串里出现问题 (引入了在一个fail数组【即Trie树上的kmp数组】)

那么这个新引入的fail数组是什么:
看张动图就明白

摘自:大佬ppt

http://files.cnblogs.com/files/crazyacking/AC%E8%87%AA%E5%8A%A8%E6%9C%BA%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8.ppt 

 

 fail数组记录的是:(fa[i]是i的父节点)

假如第i号节点与fa[i]节点连边值为x,那么从fa[i]节点向上不断fa[i]=fail[fa[i]],直到发现j=fa[i]号节点存在一条与其儿子连边值为x为止,最后令fail[i]=j即可
但代码实现方式是个反的,也就是说研究对象是u=fa[i]如果发现trie[u][x]=0,那么直接赋值trie[u][x]=trie[fail[u]][x],提前上升为下面子节点服务,否则直接fail[trie[u][x]]=trie[fail[u][x]]赋值即可

AC自动机代码实现分三个部分:
void insert(char *s) //向Trie树中插入一个模式串
void build() //待所有模式串插入后,建立fail数组
int qry(char *s) //询问文本串在Trie的匹配

搬运大佬代码:
https://www.luogu.org/blog/zcysky/solution-p3808

queue<int>q;
struct Tree{
int trie[N][26],sum[N],fail[N],tot=0;
//插入一个模式串
void insert(char *s){
int len=strlen(s),now=0;
For(i,0,len-1){
int id=s[i]-'a';
if(!trie[now][id])trie[now][id]=++tot;
now=trie[now][id];
}
sum[now]++;
}
//初始化fail数组
void build(){
For(i,0,25)if(trie[0][i])fail[trie[0][i]]=0,q.push(trie[0][i]);
while(!q.empty()){
int u=q.front();q.pop();
For(i,0,25)
if(trie[u][i])fail[trie[u][i]]=trie[fail[u][i]],q.push(trie[u][i]);
else trie[u][i]=trie[fail[u]][i];
}}
//查询
int qry(char *s){
int len=strlen(s),now=0,ans=0;
For(i,0,len-1){
now=trie[now][s[i]-'a'];
for(int t=now;t!=0&&~sum[t];t=fail[t]){
ans+=sum[t];
sum[t]=-1;
}
return ans;
}
}AC;

main:
For(i,1,n)cin>>str,AC.insert(str);
AC.build();
cin>>str;
cout<<AC.qry(str);
原文地址:https://www.cnblogs.com/planche/p/9386157.html