Description
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。
任务:
请写一个程序:
l 读入病毒代码;
l 判断是否存在一个无限长的安全代码;
l 将结果输出
Input
第一行包括一个整数n,表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。
Output
你应在在文本文件WIN.OUT的第一行输出一个单词:
l TAK——假如存在这样的代码;
l NIE——如果不存在。
Solution
考场上一眼AC自动机
然后没搞出来。
其实就水得一匹。
但我字符串也是水得一匹。
以nxt数组为图。
很容易有从根开始的链必然不可以。
也易知如果有一个不包括root的环就可以一直跑跑跑,即是无限长的安全代码。
那么直接大力搜索判环就完了。
Code
#include<bits/stdc++.h>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
using namespace std;
int nxt[200010][2];
int fail[200010],n;
int head[200010],cnt;
bool vir[200010];
bool vis[200010];
bool ins[200010];
bool flag=0;
struct data{
int u,s;
};
struct trie{
int root,len;
int newnode(){
memset(nxt[len],0,sizeof(nxt[len]));
return (++len)-1;
}
void init(){
len = 0;
root = newnode();
}
void insert(char s[],int num){
int len = strlen(s);
int u = root;
for(int i = 0;i < len;i++){
int r=s[i]-'0';
if(!nxt[u][r])
nxt[u][r] = newnode();
u = nxt[u][r];
}
vir[u]=1;
}
void get_fail(){
queue<int>q;
fail[root] = root;
for(int i = 0;i < 2;i++){
if(!nxt[root][i])
nxt[root][i] = root;
else{
fail[nxt[root][i]] = root;
q.push(nxt[root][i]);
}
}
while(!q.empty()){
int u = q.front(); q.pop();
for(int i = 0;i < 2;i++){
if(!nxt[u][i])
nxt[u][i] = nxt[fail[u]][i];
else{
int v=fail[u];
while(v&&!nxt[v][i]) v=fail[v];
fail[nxt[u][i]]=nxt[v][i];
vir[nxt[u][i]]|=vir[nxt[v][i]];
q.push(nxt[u][i]);
}
}
}
}
bool dfs(int x){
ins[x]=1;
for(int i=0;i<2;i++){
int v=nxt[x][i];
if(ins[v]) return 1;
if(vis[v]||vir[v]) continue;
vis[v]=1;
if(dfs(v)) return 1;
}
ins[x]=0;
return 0;
}
}tree;
char s[200010];
int main(){
tree.init();
scanf("%d",&n);
for(int i = 0;i < n;i++){
scanf("%s",s);
tree.insert(s,i);
}
tree.get_fail();
flag=tree.dfs(0);
printf("%s",flag?"TAK":"NIE");
}