BZOJ2938: [Poi2000]病毒(AC自动机)

2938: [Poi2000]病毒

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 1678  Solved: 849
[Submit][Status][Discuss]

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自动机,构fail指针,然后如果在避开单词End的情况下存在环,说明可以无限下去。 tarjan求无向图环的时候需要一个队列维护,以免横边,这里也是。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=30010;
int ch[maxn][2],End[maxn],q[maxn],fail[maxn];
int cnt,Flag,head,tail,vis[maxn],inq[maxn]; char c[maxn];
void insert()
{
    int L=strlen(c+1),Now=0;
    rep(i,1,L) {
        if(!ch[Now][c[i]-'0']) ch[Now][c[i]-'0']=++cnt;
        Now=ch[Now][c[i]-'0'];
    }
    End[Now]=1;
}
void buildfail()
{
    rep(i,0,1) if(ch[0][i]) q[++head]=ch[0][i];
    while(tail<head){
        int Now=q[++tail];
        rep(i,0,1){
            if(ch[Now][i]){
                fail[ch[Now][i]]=ch[fail[Now]][i];
                q[++head]=ch[Now][i];
                End[ch[Now][i]]|=End[fail[ch[Now][i]]];
            }
            else ch[Now][i]=ch[fail[Now]][i];
        }
    }
}
void dfs(int Now)
{
    if(Flag||vis[Now]) return ;
    if(inq[Now]){ Flag=true; return ;}
    inq[Now]=1;
    if(!End[ch[Now][0]]) dfs(ch[Now][0]);
    if(!End[ch[Now][1]]) dfs(ch[Now][1]);
    vis[Now]=1; inq[Now]=0;
}
int main()
{
    int N; scanf("%d",&N);
    rep(i,1,N){
        scanf("%s",c+1);
        insert();
    }
    buildfail();
    dfs(0);
    if(Flag) puts("TAK");
    else puts("NIE");
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hua-dong/p/9985526.html