bzoj 2938: [Poi2000]病毒
2938: [Poi2000]病毒
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 1111 Solved: 556
[Submit][Status][Discuss]
Description
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。
任务:
请写一个程序:
l 读入病毒代码;
l 判断是否存在一个无限长的安全代码;
l 将结果输出
Input
第一行包括一个整数n,表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。
Output
你应在在文本文件WIN.OUT的第一行输出一个单词:
l TAK——假如存在这样的代码;
l NIE——如果不存在。
Sample Input
3
01
11
00000
01
11
00000
Sample Output
NIE
/* 首先我们把所有串建一个AC自动机 方便起见我们直接把fail指针合并到子结点 如果一个串能无限长,也就是说它可以在AC自动机上一直进行匹配但就是匹配不上 也就是说匹配指针不能走到val为1的结点,设这个点为x 即root..x是一个病毒串 那么fail指针指向x的y也不能走 因为root..x是root..y的一个后缀 处理出来判断有向图是否有环 dfs即可 */ #include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; #define maxn 30010 int n,sz=1,a[maxn][2],fail[maxn],word[maxn]; bool ins[maxn],vis[maxn]; char s[maxn]; void insert(){ int now=1,len=strlen(s); for(int i=0;i<len;i++){ int t=s[i]-'0'; if(!a[now][t])a[now][t]=++sz; now=a[now][t]; } word[now]=1; } void acmach(){ queue<int>q; q.push(1);fail[1]=0; while(!q.empty()){ int now=q.front();q.pop(); for(int i=0;i<2;i++){ if(!a[now][i]){a[now][i]=a[fail[now]][i];continue;} int k=fail[now]; while(!a[k][i])k=fail[k]; fail[a[now][i]]=a[k][i]; word[a[now][i]]|=word[a[k][i]]; q.push(a[now][i]); } } } bool dfs(int x){//判断是否有环 ins[x]=1; for(int i=0;i<2;i++){ if(ins[a[x][i]])return 1; if(vis[a[x][i]]||word[a[x][i]])continue; vis[a[x][i]]=1; if(dfs(a[x][i]))return 1; } ins[x]=0; return 0; } int main(){ for(int i=0;i<2;i++)a[0][i]=1; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",s); insert(); } acmach(); if(dfs(1))printf("TAK"); else printf("NIE"); return 0; }