Problem
给出 n n n 个 01 01 01 串,我们称这些串为病毒代码,询问是否存在一个无限长的 01 01 01 串,使得任何一个病毒代码都不是它的子串。若存在输出 TAK,否则输出 NIE。
例如,如果 { 011 , 11 , 00000 } \{011, 11, 00000\} {011,11,00000} 为病毒代码段,那么一个可能的无限长的 01 01 01 串就是 010101 ⋯ 010101\cdots 010101⋯;如果 { 01 , 11 , 000000 } \{01, 11, 000000\} {01,11,000000} 为病毒代码段,那么就不存在一个无限长的 01 01 01 串。
Solution
我们先把这 n n n 个串的AC自动机建出来。
由于病毒代码不能是它的字串,所以我们在匹配的时候就不能走到尾结点(走到尾结点就相当于匹配了一个病毒代码)。
又因为这是一个无限长的 01 01 01 串,那么在AC自动机上应该存在一个没有尾结点的环使它一直在环上跳。
因此我们打任务就是判断建好的AC自动机上是否有没有尾结点的环。
于是,就用 d f s dfs dfs 遍历AC自动机,再用一个栈来记录当前链的点,如果 d f s dfs dfs 到一个已经在栈里的点,就说明有环。
Code
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 30005
using namespace std;
int tot=0;
char S[N];
struct Trie{
int son[2];
int fail,end;
}a[N];
bool vis[N],insta[N];
void insert(){
int i,p=0;
int l=strlen(S);
for(i=0;i<l;++i){
int x=S[i]-'0';
if(!a[p].son[x])
a[p].son[x]=++tot;
p=a[p].son[x];
}
a[p].end=1;
}
void Get_fail(){
int x,i;
queue<int>Q;
for(i=0;i<2;++i){
if(a[0].son[i]){
a[a[0].son[i]].fail=0;
Q.push(a[0].son[i]);
}
}
while(!Q.empty()){
x=Q.front();Q.pop();
for(i=0;i<2;++i){
if(a[x].son[i]){
Q.push(a[x].son[i]);
a[a[x].son[i]].fail=a[a[x].fail].son[i];
if(a[a[x].fail].end) a[x].end=1;
}
else a[x].son[i]=a[a[x].fail].son[i];
}
}
}
void dfs(int x){
if(a[x].end) return;
if(insta[x]) puts("TAK"),exit(0);
if(vis[x]) return;
vis[x]=insta[x]=true;
for(int i=0;i<2;++i)
if(a[x].son[i]) dfs(a[x].son[i]);
insta[x]=false;
}
int main(){
int n,i;
scanf("%d",&n);
for(i=1;i<=n;++i){
scanf("%s",S);
insert();
}
Get_fail(),dfs(0);
puts("NIE");
return 0;
}