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");
}
posted @ 2025-04-07 23:11  整齐的艾萨克  阅读(30)  评论(0)    收藏  举报