病毒(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

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

 

posted @ 2017-03-14 22:11  karles~  阅读(509)  评论(1编辑  收藏  举报