曾经以为AC自动机是个很难的东西,但本质上,就是Trip+KMP。
AC自动机的运用
用多个模式串来匹配主串。
AC自动机的步骤
1、建立Trie
略
2、求出fail指针
什么是fail指针?
先看张图。
假设模式串分别为abcd,bcd,cd,d
其中红色的代表fail指针
fail指针就是指向当前所表示字符串的最长后缀字符串
这个定义不好看……看图理解。
仔细观察会发现和KMP差不多。
预处理fail指针时,用BFS就行了。
3、匹配
也是类似于KMP。
但是要注意,假如现在匹配到Trie中的节点为t,要试着从t开始,跳到t.fail,直到跳到根。在跳的过程中统计答案。
例题
Keywords Search
懒得将题目copy过来了……
代码
using namespace std;
#include <iostream>
#include <cstring>
#include <algorithm>
int n;
char str[53];
struct Node
{
int c[26];
int fail;
int num;
} d[500002];
int cnt;
int q[500002];
char s[1000003];
inline void init();
inline void insert();
inline void build();
inline void ac();
int ans=0;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
while (T--)
{
cin>>n;
init();
for (int i=1;i<=n;++i)
{
cin>>str;
insert();
}
build();
cin>>s;
ac();
cout<<ans<<'
';
}
return 0;
}
inline void init()
{
cnt=1;
memset(d,0,sizeof d);
d[1].fail=1;
}
inline void insert()
{
int t=1;
for (char *p=str;*p;++p)
{
int key=*p-'a';
if (!d[t].c[key])
d[t].c[key]=++cnt;
t=d[t].c[key];
}
++d[t].num;
}
inline void build()
{
d[1].fail=1;
int h=-1,t=0;
q[0]=1;
do
{
++h;
for (int i=0;i<26;++i)
if (d[q[h]].c[i])
{
if (q[h]!=1)
{
int p=d[q[h]].fail;
while (p!=1)
{
if (d[p].c[i])
break;
p=d[p].fail;
}
d[d[q[h]].c[i]].fail=(d[p].c[i]?d[p].c[i]:1);
}
else
d[d[q[h]].c[i]].fail=1;
q[++t]=d[q[h]].c[i];
}
}
while (h!=t);
}
inline void ac()
{
ans=0;
int t=1;
for (char *ch=s;*ch;++ch)
{
int key=*ch-'a';
while (t!=1 && !d[t].c[key])
t=d[t].fail;
if (d[t].c[key])
t=d[t].c[key];
for (int i=t;i!=1;i=d[i].fail)
if (d[i].num>=0)
{
ans+=d[i].num;
d[i].num=-1;
}
else
break;
}
}