[BZOJ2938]:[Poi2000]病毒(AC自动机)

题目传送门


题目描述

二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。
请写一个程序,读入病毒代码,判断是否存在一个无限长的安全代码,将结果输出。

输入格式

第一行包括一个整数n,表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。

输出格式

第一行输出一个单词。假如存在这样的代码,则输出TAK,否则输出NIE

样例

样例输入:
3
01
11
00000
样例输出:
NIE

数据范围与提示

对于全部数据,所有病毒代码段的总长度不超过3×104

题解

一看是多模式串,首先应该想到是AC自动机。

如果还不会AC自动机,可以转到这篇博客,个人感觉还是写的挺清楚的:AC自动机讲解+[HDU2222]:Keywords Search(AC自动机)

那么我们考虑怎么去处理。

显然如果存在这么一个无限长的安全串的话,说明它在AC自动机里一直匹配不上,也就是说我们需要找到一个环,这个环中的每一个点都没有任何串在这个点结束。

首先,将所有的文字段压入AC自动机,然后搞Fail指针,基本操作,不再赘述。

end数组的含义为:第i个点有没有串在这里结束,有则为1,没有则为0

不过需要注意的是,如果一个点的Fail指针指向的点的end1,那么它也要为1,因为如果它的Fail指针指向的点的end1的话,说明从根结点到Fail所组成的串是从跟节点到所组成的串的后缀。
这些都处理好之后,我们就看着是不是一个环就好了。

代码时刻

#include<bits/stdc++.h>
using namespace std;
int trie[30001][2],cnt=1;
char s[30001];
bool end[30001],flag[30001],vis[30001];
int nxt[30001],que[30001];
void insert(char *str)//构建Trie树
{
	int p=1;
	int len=strlen(str);
	for(int i=0;i<len;i++)
	{
		int ch=str[i]-'0';
		if(!trie[p][ch])trie[p][ch]=++cnt;
		p=trie[p][ch];
	}
	end[p]=1;//结尾点标记为1
}
void build()
{
	for(int i=0;i<2;i++)
		trie[0][i]=1;
	que[1]=1;
	for(int head=1,tail=1;head<=tail;head++)
		for(int i=0;i<2;i++)
			if(!trie[que[head]][i])trie[que[head]][i]=trie[nxt[que[head]]][i];
			else
			{
				que[++tail]=trie[que[head]][i];
				nxt[trie[que[head]][i]]=trie[nxt[que[head]]][i];
				end[trie[que[head]][i]]|=end[nxt[trie[que[head]][i]]];//如果end[nxt[x]]的话,说明root->x是root->fail后缀。
			}
}
bool find(int x)
{
	vis[x]=1;
	for(int i=0;i<2;i++)
	{
		if(vis[trie[x][i]])return 1;//如果这个点被访问过了,那么说明出现了一个环。
		if(end[trie[x][i]]||flag[trie[x][i]])continue;//发现这条路走不下去(end为1),或者是已经被走过了(flag为1)。
		flag[trie[x][i]]=1;//标记已经走过了这个点。
		if(find(trie[x][i]))return 1;//接着走下一个点。
	}
	vis[x]=0;
	return 0;
}
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s);
		insert(s);
	}
	build();
	if(find(0))printf("TAK");
	else printf("NIE");
	return 0;
}

rp++

posted @ 2019-06-25 12:02  HEOI-动动  阅读(151)  评论(0编辑  收藏  举报