2938: [Poi2000]病毒
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 345 Solved: 176
[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
301
11
00000
Sample Output
NIE
问能否找出一个无限的不出现的给出序列的序列。
其实就是是否可以在Trie安全图内无限走,就是是否有环。
基本思路就是把Trie安全图提出来,拓扑判断是否有环(判环的问题需要整理一下)
实现好乱。呜呜呜
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
int n,tot=1,ch[100005][2],val[100005],fail[100005];//Trie
int num,in[100005],head[100005],cnt,pre[100005],to[100005],V;//safe graph
char s[30005];
bool b[100005];
inline int idx(char ch){return ch-'0';}
void addedge(int x,int y)
{
in[y]++;to[++cnt]=y;pre[cnt]=head[x];head[x]=cnt;
}
void insert(char *s)
{
int k=1,len=strlen(s);
for (int i=0;i<len;i++)
{
int id=idx(s[i]);
if (ch[k][id]==0) ch[k][id]=++tot;
k=ch[k][id];
}
val[k]=1;
}
void getfail()
{
queue<int> q;
q.push(1);
while (!q.empty())
{
int x=q.front(),y,j;q.pop();
val[x]|=val[fail[x]];
for (int i=0;i<=1;i++)
{
j=fail[x];
while (j&&!ch[j][i]) j=fail[j];
if (ch[x][i])
{
fail[y=ch[x][i]]=j?ch[j][i]:1;
q.push(y);
}
else ch[x][i]= (j?ch[j][i]:1);
}
}
}
bool circle()
{
queue<int> q;
int sum=0;
for (int i=1;i<=tot;i++)
{
if (val[i]) sum++;
else for (int j=0;j<=1;j++)
if (!val[ch[i][j]])
addedge(i,ch[i][j]);
}
for (int i=1;i<=tot;i++) if (!val[i]&&!in[i]) q.push(i);
while (!q.empty())
{
int x=q.front();q.pop();sum++;
for(int i=head[x];i;i=pre[i])
{
int y=to[i];
in[y]--;
if (!in[y]) q.push(y);
}
}
return sum==tot;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%s",s);
insert(s);
}
getfail();
if (circle()) printf("NIE");else printf("TAK");
return 0;
}