4.8 AC 自动机
AC 自动机
OI-Wiki 上说:
AC(Aho–Corasick)自动机是 以 Trie 的结构为基础,结合 KMP 的思想 建立的自动机,用于解决多模式匹配等任务。
然而不会 KMP 似乎并不影响对 AC 自动机的学习。
由于本文的重点并不是讲解 AC 自动机的板子,所以下文直接给出例题。
P2444 [POI 2000] 病毒
一道很经典的题。
题意
给若干 01 串,询问是否能构造出无限长的串,使得给出的任意一个串都不是这个无限串的子串。
题解
考虑对所给的 01 串建立 AC 自动机,把所有 01 串结束的节点打上标记。
联想在它上面匹配一个串的过程,可以发现,只要走到了一个被标记的节点,那么这个串就是不合法的。
并且,即使一个节点没有被打上标记,如果这个节点的 fail 链上有被标记的节点,那么走到这个节点的串也是不合法的。
因此,不仅要把 01 串结束的节点打上标记,也要把能从 fail 链跳到这样的节点的节点打上标记。
我们要寻找一个无限的合法 01 串。什么情况下会出现无限的合法 01 串呢?根据灵性!我们对 AC 自动机的了解,发现只需要在这个 AC 自动机上找到一个环就可以了。
于是做法便明晰了:对 01 串建 AC 自动机,DFS 找环。
实现细节方面,注意区分“当前正在搜的路径”和“当前节点被没被搜过”的标记。
#include <iostream>
#define N 300005
struct Trie
{
int p[N][2],cnt;
bool end[N],vis[N],v2[N];
int fail[N];
int q[N];
void insert(std::string s)
{
int u=0;
for(int i=0;i<s.size();i++)
{
if(!p[u][s[i]-'0']) p[u][s[i]-'0']=++cnt;
u=p[u][s[i]-'0'];
}
end[u]=1;
}
void build()
{
int hd=1,tl=0;
for(int i=0;i<2;i++) if(p[0][i]) q[++tl]=p[0][i];
while(hd<=tl)
{
for(int i=0;i<2;i++)
if(p[q[hd]][i])
{
fail[p[q[hd]][i]]=p[fail[q[hd]]][i];
if(end[p[fail[q[hd]]][i]]) end[p[q[hd]][i]]=1;
q[++tl]=p[q[hd]][i];
}
else p[q[hd]][i]=p[fail[q[hd]]][i];
hd++;
}
}
bool dfs(int x)
{
vis[x]=1;
for(int i=0;i<2;i++)
{
if(vis[p[x][i]]) return 1;
if(end[p[x][i]]) continue;
if(!v2[p[x][i]])
{
v2[p[x][i]]=1;
if(dfs(p[x][i])) return 1;
}
}
vis[x]=0;
return 0;
}
} tr;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
std::string s;
std::cin>>s;
tr.insert(s);
}
tr.build();
if(tr.dfs(0)) printf("TAK");
else printf("NIE");
}

浙公网安备 33010602011771号