【POI2000】病毒

【POI2000】病毒

by AmanoKumiko

Description

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

示例:例如如果\([011,11,00000]\)为病毒代码段,那么一个可能的无限长安全代码就是\(010101...\)。如果\([01,11,000000]\)为病毒代码段,那么就不存在一个无限长的安全代码。

请写一个程序,读入病毒代码,判断是否存在一个无限长的安全代码,将结果输出。

Input

第一行包括一个整数\(n\),表示病毒代码段的数目;

以下的\(n\)行,每一行都包括一个非空的01字符串——就是一个病毒代码段。

Output

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

Sample Input

3
01
11
00000

Sample Output

NIE

Data Constraint

对于全部数据,所有病毒代码段的总长度不超过\(3*10^4\)

Solution

1.建自动机

2.给可能经过单词结尾的位置打上标记

3.设\(f[i][j]\)表示当前长度为\(i\),在自动机上第\(j\)个点的安全代码的存在性为\(f[i][j]\),若\(∃j,f[Σ{|S|}][j]=1\),则说明可以得到无限长的安全代码

Code

#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define L 30010

queue<int>q;
bool f[L][L];
int n,len,mx,sum;
char ch[L];
struct ACM{
	int son[L][2],flag[L],fail[L],tot,Q[L],he;
	void insert(){
		int u=1;
		F(j,1,len)son[u][ch[j]-'0']?u=son[u][ch[j]-'0']:u=son[u][ch[j]-'0']=++tot;
		flag[u]=1;
	}
	void build(){
		F(i,0,1)son[0][i]=1;q.push(1);
		while(!q.empty()){
			int u=q.front();q.pop();Q[++he]=u;
			F(i,0,1){
				int v=son[u][i],Fail=fail[u];
				if(!v){son[u][i]=son[Fail][i];continue;}
				fail[v]=son[Fail][i];
				q.push(v);
			}
		}
		F(i,1,he)flag[Q[i]]|=flag[fail[Q[i]]];//标记
	}
}t;

int main(){
	scanf("%d",&n);t.tot=1;
	F(i,1,n)scanf("%s",ch+1),len=strlen(ch+1),t.insert(),sum+=len;
	t.build();
	mx=0;
	f[0][1]=1;
	F(i,0,mx){
		F(j,1,t.tot)if(f[i][j]){
			F(k,0,1){
				int v=t.son[j][k];
				if(!t.flag[v]){
					f[i+1][v]|=f[i][j];
					mx=max(mx,i+1);
				}
			}
		}
		if(mx==sum){printf("TAK");return 0;}
	}//dp
	printf("NIE");
	return 0;
}

Another Solution(一般解法)

直接在AC自动机上搜索,在不经过单词结尾的情况下若搜出环,则可行

I’m too naive.

posted @ 2021-02-04 19:22  冰雾  阅读(107)  评论(0编辑  收藏  举报