BZOJ2938 [Poi2000]病毒

BZOJ2938 [Poi2000]病毒

洛谷传送

Description

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

Input

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

Output

- 第一行输出一个单词:
TAK——假如存在这样的代码;
NIE——如果不存在。

Sample Input 1

- 3
01 
11 
00000

Sample Output 1

- NIE

Source

- [Poi2000]

思路

  • AC自动机模板题,将病毒串建AC自动机
  • 可以发现当AC自动机上满足如下条件时有无限长的安全代码输出 TAK
    • 不经过病毒串的结尾字符
    • 必须不经过病毒串的结尾字符所在的 \(fail\) 链上的所有字符
  • 否则输出 NIE

code

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <queue>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=2;
const int Max=30010;
int n;
char s[Max];
struct Trie
{
	int nx[Max][N],fail[Max];
	bool vis[Max],end[Max],ins[Max];
	int root,tot;
	int newnode()
	{
		for(int i=0; i<N; i++)  nx[tot][i]=-1;
		end[tot++]=false;
		return tot-1;
	}
	void init(){    tot=0,root=newnode();   }
	void insert(char s[])
	{
		int len=strlen(s),now=root,k;
		for(int i=0; i<len; i++)
		{
			k=(int)(s[i]-'0');
			if(nx[now][k]==-1)  nx[now][k]=newnode();
			now=nx[now][k];
		}
		end[now]=true;
	}
	void build()
	{
		queue<int> Q;
		fail[root]=root;
		for(int i=0; i<N; i++)
			if(nx[root][i]==-1) nx[root][i]=root;
			else    fail[nx[root][i]]=root,Q.push(nx[root][i]);
		int now;
		while(!Q.empty())
		{
			now=Q.front();Q.pop();
			for(int i=0; i<N; i++)
				if(nx[now][i]==-1)  nx[now][i]=nx[fail[now]][i];
				else
				end[nx[now][i]]=end[nx[now][i]]|end[nx[fail[now]][i]],
				//必须不经过病毒串的结尾字符所在的fail链上的所有字符
				fail[nx[now][i]]=nx[fail[now]][i],
				Q.push(nx[now][i]);
		}
	}
	bool dfs(int t)
	{
		ins[t]=true;//标记访问路径 
		for(int i=0; i<N; i++)
		{
			if(ins[nx[t][i]])	return true;
			if(end[nx[t][i]]||vis[nx[t][i]])	continue;
			vis[nx[t][i]]=true;//标记该条路径是否走过 
			if(dfs(nx[t][i]))	return true;//继续访问 
		}
		ins[t]=false;
		return 0;
	}//判断是否有满足条件的环 
	void pd()
	{
		if(dfs(0))	cout<<"TAK"<<endl;
		else	cout<<"NIE"<<endl;
	}
}ac;

int main()
{
	ac.init();
	scanf("%d",&n);
	for(int i=0; i<n; i++)
		scanf("%s",s),ac.insert(s);
	ac.build();
	ac.pd();
	return 0;
}
posted @ 2020-05-02 17:23  白菜道士  阅读(121)  评论(0编辑  收藏  举报