[bzoj2938][Poi2000]病毒——AC自动机
题目大意:
给定n个01串,求是否存在一个长度无穷的01串使得这个01串不包含任何一个给定的串。
思路:
考虑AC自动机匹配的过程是在Trie树上不停地跳,那么如果我们可以找到一个串使得这个串可以一直在Trie上跳并且永远跳不到匹配节点就说明可行。
可以发现这样的话这个串在AC自动机上的匹配一定是会出现环的,于是我们直接dfs找环就好了。
考虑如何判断这个节点是否可以走到,即这个节点以及这个节点的所有fail都没有匹配才可以走,在求fail的时候可以预处理出来。
dfs找环的时候记得要开两个bool数组,一个标记是否来过,一个标记是否在栈中。
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj2398.in","r",stdin);
freopen("bzoj2398.out","w",stdout);
}
template<typename T>void read(T &_){
_=0; T f=1; char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
_*=f;
}
const int maxn=6e4+10;
int n;
int ans[maxn],tp;
struct AC_Automaton{
int ch[maxn][2],num[maxn],fail[maxn],cnt;
bool vis[maxn],in[maxn];
void insert(char *s){
int len=strlen(s+1),u=0,c;
REP(i,1,len){
c=s[i]-'0';
if(!ch[u][c])ch[u][c]=++cnt;
u=ch[u][c];
}
++num[u];
}
void build(){
int h=1,t=0,q[maxn];
REP(i,0,1)if(ch[0][i])
q[++t]=ch[0][i];
while(h<=t){
int u=q[h++];
REP(i,0,1){
int v=ch[u][i];
if(v)q[++t]=v;
if(v){
fail[v]=ch[fail[u]][i];
num[v]|=num[fail[v]];
}
else ch[u][i]=ch[fail[u]][i];
}
}
}
bool dfs(int u){
if(num[u])return false;
if(in[u])return true;
if(vis[u])return false;
vis[u]=in[u]=1;
if(dfs(ch[u][0]) || dfs(ch[u][1]))return true;
in[u]=0;
return false;
}
}T;
int main(){
File();
char s[maxn];
read(n);
REP(i,1,n){
scanf("%s",s+1);
T.insert(s);
}
T.build();
printf("%s\n",T.dfs(0) ? "TAK" : "NIE");
REP(i,1,tp)printf("%d",ans[i]);
return 0;
}