AC自动机
AC自动机分为三个部分:
-
构造trie树
-
构造fail指针
-
模式匹配
1.构造trie树
在trie树上,从根节点开始,到某一叶子节点的路径即为一个字符串。
每个节点有n个出度,代表字符串每一位有n种情况。
如,对于二进制数,n=2 ;对于小写字母字符串,n=26 。
因此,构建trie树,只需从根节点开始,沿着字符串向下移动,并不断补充新节点即可。
最后在叶子节点上进行相应的处理即可(具体处理方法根据题意)。
以二进制为例,trie树处理结果如图:

代码:
void build_trie(char x[]){
int len=strlen(x),p=0;
for(int i=0;i<len;++i){
int t=x[i]-'a';
if(!pre[p][t])pre[p][t]=++tot;
p=pre[p][t];
}
++val[p];
}
2.构造fail指针
KMP只支持 单串与单串 之间的比较,而AC自动机通过添加fail指针,记录最大相同前缀后缀,满足 单串与多串 的比较。
对于某一节点,其子节点的fail为其fail的相同子节点。
特别的,根节点的子节点的fail为根节点。
建立了fail指针后,当某个子节点不存在时,就可以顺着fail往上跳,保证连贯性。
以二进制为例,fail指针处理结果如图:

代码:
void build_AC(){
for(int i=0;i<26;++i)
if(pre[0][i])q.push(pre[0][i]);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<26;++i){
if(pre[x][i]){
q.push(pre[x][i]);
fail[pre[x][i]]=pre[fail[x]][i];
}
else pre[x][i]=pre[fail[x]][i];
}
}
}
————————————————————
3.模式匹配
准备工作做完后,接下来开始匹配。
同样是顺着字符串往下走,与第一步不同的是,这次不能建立新节点,同时要关注fail。
因为fail记录的是最大相同前缀后缀,则fail的fail记录的是所有的相同前缀后缀。
因此,对于一个节点,只要一直往上fail,就可以访问到所有可能存在的答案。
代码:
int query_AC(char x[]){
int len=strlen(x),p=0,res=0;
for(int i=0;i<len;++i){
int t=x[i]-'a';
p=pre[p][t];
for(int j=p;j && ~val[j];j=fail[j]){
res+=val[j];val[j]=-1;
}
}
return res;
}
完成以上三个步骤,AC自动机就建好了。
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int M=1e6+5;
int n;
char s[M];
queue<int> q;
struct AC_AUTO{
int pre[M][30],fail[M],val[M],tot;
void build_trie(char x[]){
int len=strlen(x),p=0;
for(int i=0;i<len;++i){
int t=x[i]-'a';
if(!pre[p][t])pre[p][t]=++tot;
p=pre[p][t];
}
++val[p];
}
void build_AC(){
for(int i=0;i<26;++i)
if(pre[0][i])q.push(pre[0][i]);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<26;++i){
if(pre[x][i]){
q.push(pre[x][i]);
fail[pre[x][i]]=pre[fail[x]][i];
}
else pre[x][i]=pre[fail[x]][i];
}
}
}
int query_AC(char x[]){
int len=strlen(x),p=0,res=0;
for(int i=0;i<len;++i){
int t=x[i]-'a';
p=pre[p][t];
for(int j=p;j && ~val[j];j=fail[j]){
res+=val[j];val[j]=-1;
}
}
return res;
}
}AC;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%s",s);
AC.build_trie(s);
}
AC.build_AC();
scanf("%s",s);
printf("%d\n",AC.query_AC(s));
return 0;
}
\[\mathcal{By}\quad\mathcal{Most}\ \mathcal{Handsome}
\]
本文来自博客园,作者:Most_Handsome,转载请注明原文链接:https://www.cnblogs.com/MostHandsome/p/14987748.html

浙公网安备 33010602011771号