【BZOJ-2938】病毒 Trie图 + 拓扑排序

2938: [Poi2000]病毒

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 609  Solved: 318
[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

HINT

Source

Solution

Trie图的一大经典应用。

要构造一个无限长的安全串,显然是需要找至少一个安全的子串,然后循环下去,问题在于是否存在这样的子串。

建出Trie图之后,满足条件的子串必须在Trie图上不断匹配,而且不断失配无法达到危险节点。

这就说明,Trie图中存在不经过危险节点的环! 然后进行拓扑排序即可。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 30010
int N;
char s[MAXN];
struct EdgeNode{int next,to;}edge[MAXN<<1];
int head[MAXN],cnt=1,d[MAXN],visit[MAXN];
inline void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
inline void InsertEdge(int u,int v) {AddEdge(u,v); d[v]++; /*printf("%d ---> %d\n",u,v);*/}
namespace ACMachine
{
    #define id(s) s-'0'
    int son[MAXN][2],end[MAXN],sz=1,fail[MAXN];
    inline void Insert(char str[])
    {
        int len=strlen(str+1),now=1;
        for (int i=1; i<=len; i++)
            if (son[now][id(str[i])]) now=son[now][id(str[i])];
                else son[now][id(str[i])]=++sz,now=sz;
        end[now]=1;
    }
    inline void Getfail()
    {
        queue<int>q; q.push(1);
        while (!q.empty())
            {
                int now=q.front(); q.pop(); 
                end[now]|=end[fail[now]];
                for (int i=0; i<=1; i++)
                        {
                            int fa=fail[now];
                            while (fa && !son[fa][i]) fa=fail[fa];
                            if (son[now][i]) 
                                fail[son[now][i]]=fa? son[fa][i]:1,q.push(son[now][i]);
                            else son[now][i]=fa? son[fa][i]:1;
                        }
            }
    }
}
using namespace ACMachine;
inline bool Topo()
{
    queue<int>q;
    int sum=0;
    for (int i=1; i<=sz; i++)
        {
            if (end[i]) sum++; else
            for (int j=0; j<=1; j++)
                if (!end[son[i][j]]) InsertEdge(i,son[i][j]);
        }
    for (int i=1; i<=sz; i++) if (!d[i] && !end[i]) q.push(i);
    while (!q.empty())
        {
            int now=q.front(); q.pop(); sum++;
            for (int i=head[now]; i; i=edge[i].next)
                if (!--d[edge[i].to]) q.push(edge[i].to);
        }
    return sum==sz;
}
int main()
{
    scanf("%d",&N);
    for (int i=1; i<=N; i++) scanf("%s",s+1),Insert(s);
    Getfail();
    if (Topo()) puts("NIE"); else puts("TAK");
    return 0;
}

 

posted @ 2016-12-06 20:53  DaD3zZ  阅读(733)  评论(0编辑  收藏  举报