【BZOJ-2938】病毒 Trie图 + 拓扑排序
2938: [Poi2000]病毒
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 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
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; }
——It's a lonely path. Don't make it any lonelier than it has to be.