ac自动机模板感悟
今天第一次打出来了ac自动机这个模板。其一直被称为树上的kmp,但我觉得这句话很容易误导人,还是有些许不同。
首先给一篇csdn上的讲解ac自动机
构造fail数组
他的核心是构造fail数组(即kmp中的nxt),用bfs的方法,先让第一层的所有结点fail只想root,同时将其入队列。入队列后的操作较为麻烦,文字描述比较苍白,我上图和代码。
void getfail()
{
queue<int> q;
for(int i=0;i<26;i++)
if(ch[0][i])
fail[ch[0][i]]=0,
q.push(ch[0][i]);
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=0;i<26;i++)
{
if(ch[u][i])
fail[ch[u][i]]=ch[fail[u]][i],
q.push(ch[u][i]);
else
ch[u][i]=ch[fail[u]][i];
}
}
}
处理询问
还有一个要注意的地方是询问时,要把走过的地方标为-1,确保不会再走(这个好像是一个剪枝的优化)
int ans=0,x=0;
for(int i=0;i<txt.size();i++)
{
int c=txt[i]-'a';
x=ch[x][c];
for(int j=x;j&&num[j]!=-1;j=fail[j])
{
ans+=num[j];
num[j]=-1;
}
}
最后有一个小地方,就是在这道题里,string比char数组快(卡了笔者两个小时,佛了)orz
全部代码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 10000000
int n,ch[MAXN][30],fail[MAXN],tot;
string s,txt;
int num[MAXN];
void insert(string s)
{
int len=s.size(),x=0;
for(int i=0;i<len;i++)
{
int c=s[i]-'a';
if(!ch[x][c]) ch[x][c]=++tot;
x=ch[x][c];
}
num[x]++;
}
void getfail()
{
queue<int> q;
for(int i=0;i<26;i++)
if(ch[0][i])
fail[ch[0][i]]=0,
q.push(ch[0][i]);
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=0;i<26;i++)
{
if(ch[u][i])
fail[ch[u][i]]=ch[fail[u]][i],
q.push(ch[u][i]);
else
ch[u][i]=ch[fail[u]][i];
}
}
}
int main()
{
// freopen("r","2.in",stdin);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>s;
insert(s);
}
getfail();
cin>>txt;
int ans=0,x=0;
for(int i=0;i<txt.size();i++)
{
int c=txt[i]-'a';
x=ch[x][c];
for(int j=x;j&&num[j]!=-1;j=fail[j])
{
ans+=num[j];
num[j]=-1;
}
}
cout<<ans;
}