病毒(bzoj 2938)
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自动机,在不能走每个单词最后一个字母的前提下,如果某个字符串在自动机上无法匹配, 则说明可以找到无限长的字符串。 为了方便起见,将所有的point指针合并,最后在自动机上搜索就行了。 */ #include<iostream> #include<cstdio> #include<queue> #include<cstring> #define N 30010 using namespace std; int a[N][2],point[N],danger[N],ins[N],vis[N],n,size=1; char s[N]; queue<int> q; void insert(){ int len=strlen(s),now=1; for(int i=0;i<len;i++){ int t=s[i]-'0'; if(!a[now][t]) a[now][t]=++size; now=a[now][t]; } danger[now]=1; } void build(){ q.push(1);point[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[point[now]][i]; continue; } int k=point[now]; while(!a[k][i]) k=point[k]; point[a[now][i]]=a[k][i]; danger[a[now][i]]|=danger[point[a[now][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 true; if(vis[a[x][i]]||danger[a[x][i]]) continue; vis[a[x][i]]=1; if(dfs(a[x][i])) return true; } ins[x]=0; return false; } int main(){ scanf("%d",&n); for(int i=0;i<2;i++) a[0][i]=1; for(int i=1;i<=n;i++){ scanf("%s",s); insert(); } build(); if(dfs(1)) printf("TAK"); else printf("NIE"); return 0; }