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

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");
}

 

posted @ 2018-04-20 16:44  fcwww  阅读(238)  评论(0编辑  收藏  举报