AC自动机
\(前置cheese\)
一、简介:
与\(KMP\)类似,\(AC\)自动机也是用来处理字符串匹配的问题。与\(KMP\)不同的是,\(KMP\)用来处理单模式串问题,即问模式串\(T\)是否是其他主串\(S_i\)的子串,而\(AC\)自动机则能处理多模式串的问题.\(AC\)自动机处理的常见问题如:给出\(n\)个单词\(T_i\),再给出一段包含\(m\)个字符的文章\(S\),问有多少个单词在文章里出现了。
构建一个\(AC\)自动机并用于匹配需要三个步骤:将所有的模式串构建成一棵\(Trie\),对\(Trie\)上所有的节点构造失败指针\((fail)\),利用失败指针对主串进行模式匹配。实际上这个失败指针与\(KMP\)算法中的\(next\)数组非常相似,因而\(AC\)自动机可以看做是\(Trie\)与\(KMP\)算法的结合(\(Trie\)上的\(KMP\)算法)。
我们将模式串所构出的\(Trie\)的节点称为\(AC\)自动机的节点(状态),\(Trie\)中的边称为\(AC\)自动机中的边(转移),将失配指针所对应的节点称为失配转移。
二、算法流程:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,cnt=0;
char tmp[1000100];
struct node
{
int fail,num;
int ch[30];
}tr[1000100];
int q[1000100];
void build()
{
int len=strlen(tmp),u=0;
for(int i=0;i<len;i++)
{
int s=tmp[i]-'a';
if(tr[u].ch[s]==0) tr[u].ch[s]=++cnt;
u=tr[u].ch[s];
}
tr[u].num++;
}
void get_fail()
{
int l=0,r=0;
for(int i=0;i<26;i++)
if(tr[0].ch[i]!=0)
{
tr[tr[0].ch[i]].fail=0;
q[++r]=tr[0].ch[i];
}
while(l<r)
{
int u=q[++l];
for(int i=0;i<26;i++)
{
int v=tr[u].ch[i];
if(v)
{
tr[v].fail=tr[tr[u].fail].ch[i];//处理fail指针,是它父亲的fail指针的i儿子
q[++r]=v;
}
else tr[u].ch[i]=tr[tr[u].fail].ch[i];//处理所说的“虚指针”,WA了好几次因为把这里的tr[u].ch[i]也替换成v了。。
}
}
}
int AC()
{
int len=strlen(tmp);
int u=0,ans=0;
for(int i=0;i<len;i++)
{
int s=tmp[i]-'a';
u=tr[u].ch[s];
int v=u;
while(v && tr[v].num!=-1)
{
ans+=tr[v].num;
tr[v].num=-1;//加过了之后就不用再加了
v=tr[v].fail;
}
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",tmp);
build();
}
tr[0].fail=0;
get_fail();
scanf("%s",tmp);
printf("%d",AC());
return 0;
}
浙公网安备 33010602011771号