【BZOJ2938】[Poi2000]病毒
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合并到儿子节点上,删去单词的结束点,然后用DFS看一下Trie树里有没有环就行了,有环就是TAK
也可以用拓补排序判环,但是比较难搞,因为不知道入度什么时候为0
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; const int maxn=30010; struct node { int ch[2],fail,cnt; }p[maxn]; int n,tot,ans; char str[maxn]; int vis[maxn],ins[maxn]; queue<int> q; void dfs(int x) { int i; ins[x]=vis[x]=1; for(i=0;i<=1;i++) { if(p[p[x].ch[i]].cnt) continue; if(!vis[p[x].ch[i]]) dfs(p[x].ch[i]); else if(ins[p[x].ch[i]]) ans=1; } ins[x]=0; } int main() { scanf("%d",&n); int i,j,k,u,t; tot=1; for(i=1;i<=n;i++) { scanf("%s",str); k=strlen(str); u=1; for(j=0;j<k;j++) { if(!p[u].ch[str[j]-'0']) p[u].ch[str[j]-'0']=++tot; u=p[u].ch[str[j]-'0']; } p[u].cnt=1; } q.push(1); while(!q.empty()) { u=q.front(),q.pop(); for(i=0;i<=1;i++) { if(p[u].ch[i]) { q.push(p[u].ch[i]); if(u==1) p[p[u].ch[i]].fail=1; else p[p[u].ch[i]].fail=p[p[u].fail].ch[i]; p[p[u].ch[i]].cnt|=p[p[p[u].fail].ch[i]].cnt; } else { if(u==1) p[u].ch[i]=1; else p[u].ch[i]=p[p[u].fail].ch[i]; } } } dfs(1); if(ans==1) printf("TAK"); else printf("NIE"); return 0; }