【POI2000】病毒
【POI2000】病毒
by AmanoKumiko
Description
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:例如如果\([011,11,00000]\)为病毒代码段,那么一个可能的无限长安全代码就是\(010101...\)。如果\([01,11,000000]\)为病毒代码段,那么就不存在一个无限长的安全代码。
请写一个程序,读入病毒代码,判断是否存在一个无限长的安全代码,将结果输出。
Input
第一行包括一个整数\(n\),表示病毒代码段的数目;
以下的\(n\)行,每一行都包括一个非空的01字符串——就是一个病毒代码段。
Output
第一行输出一个单词。假如存在这样的代码,则输出 TAK
,否则输出 NIE
。
Sample Input
3
01
11
00000
Sample Output
NIE
Data Constraint
对于全部数据,所有病毒代码段的总长度不超过\(3*10^4\)。
Solution
1.建自动机
2.给可能经过单词结尾的位置打上标记
3.设\(f[i][j]\)表示当前长度为\(i\),在自动机上第\(j\)个点的安全代码的存在性为\(f[i][j]\),若\(∃j,f[Σ{|S|}][j]=1\),则说明可以得到无限长的安全代码
Code
#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define L 30010
queue<int>q;
bool f[L][L];
int n,len,mx,sum;
char ch[L];
struct ACM{
int son[L][2],flag[L],fail[L],tot,Q[L],he;
void insert(){
int u=1;
F(j,1,len)son[u][ch[j]-'0']?u=son[u][ch[j]-'0']:u=son[u][ch[j]-'0']=++tot;
flag[u]=1;
}
void build(){
F(i,0,1)son[0][i]=1;q.push(1);
while(!q.empty()){
int u=q.front();q.pop();Q[++he]=u;
F(i,0,1){
int v=son[u][i],Fail=fail[u];
if(!v){son[u][i]=son[Fail][i];continue;}
fail[v]=son[Fail][i];
q.push(v);
}
}
F(i,1,he)flag[Q[i]]|=flag[fail[Q[i]]];//标记
}
}t;
int main(){
scanf("%d",&n);t.tot=1;
F(i,1,n)scanf("%s",ch+1),len=strlen(ch+1),t.insert(),sum+=len;
t.build();
mx=0;
f[0][1]=1;
F(i,0,mx){
F(j,1,t.tot)if(f[i][j]){
F(k,0,1){
int v=t.son[j][k];
if(!t.flag[v]){
f[i+1][v]|=f[i][j];
mx=max(mx,i+1);
}
}
}
if(mx==sum){printf("TAK");return 0;}
}//dp
printf("NIE");
return 0;
}
Another Solution(一般解法)
直接在AC自动机上搜索,在不经过单词结尾的情况下若搜出环,则可行
I’m too naive.