BZOJ_2938_[Poi2000]病毒_AC自动机
BZOJ_2938_[Poi2000]病毒_AC自动机
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
$trie$ 图真的是比$AC$ 自动机好写多了。
存在这样的字符串,当且仅当在$trie$ 图上存在一个不包括病毒代码的环。
具体操作:建个$trie$ 树,终止处打上标记,在建$fail$ 的时候把子树也标记上。
代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 2050 #define M 60050 char w[M]; int ch[M][2],cnt[M],tot=1,n,Q[M],l,r,fail[M],vis[M],ins[M]; void insert() { int p=1,i; for(i=0;w[i];i++) { int &k=ch[p][w[i]-'0']; if(!k) k=++tot; p=k; } cnt[p]=1; } void build() { int p,i; for(i=0;i<2;i++) ch[0][i]=1; Q[r++]=1; while(l<r) { p=Q[l++]; for(i=0;i<2;i++) { if(ch[p][i]) fail[ch[p][i]]=ch[fail[p]][i],Q[r++]=ch[p][i]; else ch[p][i]=ch[fail[p]][i]; cnt[p]|=cnt[fail[p]]; } } } bool dfs(int x) { vis[x]=1; ins[x]=1; int i,t; for(i=0;i<2;i++) { t=ch[x][i]; if(ins[t]||(!vis[t]&&!cnt[t]&&dfs(t))) return 1; } ins[x]=0; return 0; } int main() { scanf("%d",&n); int i; for(i=1;i<=n;i++) { scanf("%s",w); insert(); } build(); puts(dfs(1)?"TAK":"NIE"); }