[POI2000]病毒
题目描述
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。
任务:
请写一个程序:
1.在文本文件WIR.IN中读入病毒代码;
2.判断是否存在一个无限长的安全代码;
3.将结果输出到文件WIR.OUT中。
solution
首先我们可以建出AC自动机
我们如果我们可以在ac自动机上找到一个环,且环上没有标记,那么我们就可以一直沿着它走了。
用dfs找环就可以了。
要注意一个,如果有一个点的fail被标记了,那么这个点也是不合法的(从根走到它一定会经过标记点)
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 30005
using namespace std;
int n,tr[maxn][2],cnt,e[maxn],fail[maxn];
char ch[maxn];
bool vis[maxn],ins[maxn],fsy;
void inst(){
int len=strlen(ch),p=0;
for(int i=0;i<len;i++){
if(!tr[p][ch[i]-'0'])tr[p][ch[i]-'0']=++cnt;
p=tr[p][ch[i]-'0'];
}
e[p]=1;
}
void build(){
queue<int>q;
for(int j=0;j<2;j++)if(tr[0][j])q.push(tr[0][j]);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<2;i++){
if(tr[x][i]){
fail[tr[x][i]]=tr[fail[x]][i];
e[tr[x][i]]|=e[fail[tr[x][i]]];
q.push(tr[x][i]);
}
else tr[x][i]=tr[fail[x]][i];
}
}
}
void dfs(int x){
for(int i=0;i<2;i++){
if(!e[tr[x][i]]){
if(vis[tr[x][i]]){
if(ins[tr[x][i]]){fsy=1;return;}
}
else {
ins[tr[x][i]]=1;vis[x]=1;
dfs(tr[x][i]);
ins[tr[x][i]]=0;
}
}
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%s",&ch);inst();
}
build();
dfs(0);
if(fsy)puts("TAK");
else puts("NIE");
return 0;
}