[BZOJ2938]病毒 (AC自动机+dfs)

题目描述


 二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。

示例:
例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。
任务:
请写一个程序:
l 读入病毒代码;
l 判断是否存在一个无限长的安全代码;
l 将结果输出

 输入


第一行包括一个整数n,表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。


输出


你应在在第一行输出一个单词:

l TAK——假如存在这样的代码。
l NIE——如果不存在。

样例


样例输入

3
01
11
00000

样例输出

NIE
 
题解

多模式串匹配问题。
果断AC自动机(因为蒟蒻我也不会别的啊QAQ
把每个病毒串插进trie树,get_fail这些基本操作大家想必都会啦
本题多了一个奇诡操作:fail环。
首先了解一下fail环
拉过来一张丑陋的图:
如上图为题目描述中为NIE的那个数据(好像少了个0……)。
显然上图中虚线表示的0并不存在。
所以dfs搜到他的时候,这个点指向root的右儿子的那个1的左儿子。
不过遗憾的是
这个0也不存在。
那么只好走这个1的fail指针,即root的右儿子,
然后我们发现,它还是会从root的右儿子去找到当前这个虚线框里的0
这次这个0指向了root的左儿子。
然后再顺下来我们发现又可以找到原来的1啦。
这就是fail环啦(至少我是这么理解的,大神别踩啊QAQ)
然后捏?
我们把插入时的endd设置成危险节点,
即不能访问的节点
然后一遍dfs就出来啦!
代码:
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
#define rint register int
using namespace std;
char ch[30004];
int n;
int trie[30004][2];
int cnt=1,fail[30004];
bool endd[30004],vis[30004];
bool ans=false,failed[30004];
inline void insert(char *str)
{
	int len=strlen(str),p=1;
	for(rint i=0;i<len;++i)
	{
		int l=str[i]-'0';
		if(!trie[p][l])
			trie[p][l]=++cnt;
		p=trie[p][l];
	}
	endd[p]=true;
}
inline void get_fail()
{
	queue <int>q;
	q.push(1);
	fail[1]=0;
	trie[0][0]=trie[0][1]=1;
	while(!q.empty())
	{
		int l=q.front();q.pop();
		for(rint i=0;i<2;++i)
		{
			if(trie[l][i])
			{
				fail[trie[l][i]]=trie[fail[l]][i];
				if(endd[fail[trie[l][i]]])
					endd[trie[l][i]]=true;//注意这两句话,没加毁人生QAQ
				q.push(trie[l][i]);
			}
			else trie[l][i]=trie[fail[l]][i];
		}
	}
}
inline void dfs(int u)
{
	vis[u]=1;
	for(int i=0;i<=1;i++)
	{
		if(vis[trie[u][i]])
		{
			ans=true;
			return ;
		}
		else if(!failed[trie[u][i]]&&!endd[trie[u][i]])
		{
			failed[trie[u][i]]=1;
			dfs(trie[u][i]);
		}
	}
	vis[u]=0;
	return ;
}
int main()
{
//	freopen("wir213.in","r",stdin);
	scanf("%d",&n);
	for(rint i=1;i<=n;++i)
	{
		scanf("%s",ch);
		insert(ch);
	}
	get_fail();
//	for(rint i=1;i<=cnt;++i)cout<<fail[i]<<endl;
	dfs(1);
	if(ans)cout<<"TAK"<<endl;
	else cout<<"NIE"<<endl;
	return 0;
}

 完结撒花~

猜你喜欢

转载自www.cnblogs.com/xingmi-weiyouni/p/11082103.html