[BZOJ2938][POI2000]病毒(AC自动机)
Description
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例: 例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。 任务: 请写一个程序: (1) 读入病毒代码;
(2) 判断是否存在一个无限长的安全代码;
(3) 将结果输出
Input Format
第一行包括一个整数n,表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。
Output Format
你应在在文本文件WIN.OUT的第一行输出一个单词: (1) TAK——假如存在这样的代码; (2) NIE——如果不存在。
Solution
建AC自动机,将fail指针合并到子节点上,然后形成一个有向图,
然后在图上dfs,如果有环就是有无限长的串
Code
#include <cstdio> #include <algorithm> #include <cstring> #define N 30010 using namespace std; char s[N]; int n,T[N][2],fail[N],cnt=1,q[N]; bool mark[N],vis[N],inq[N]; inline void Insert(){ scanf("%s\n",s); int len=strlen(s),now=1; for(int i=0;i<len;++i){ if(!T[now][s[i]-'0']) T[now][s[i]-'0']=++cnt; now=T[now][s[i]-'0']; } mark[now]=1; } inline void getfail(){ int h=0,t=0,now;q[++t]=1; for(int i=0;i<2;++i) T[0][i]=1; while(h<t){ now=q[++h]; for(int i=0;i<2;++i) if(T[now][i]){ int k=fail[now]; while(!T[k][i]) k=fail[k]; fail[q[++t]=T[now][i]]=T[k][i]; mark[T[now][i]]|=mark[T[k][i]]; }else T[now][i]=T[fail[now]][i]; } } bool dfs(int u){ inq[u]=1; for(int i=0;i<2;++i){ int v=T[u][i]; if(inq[v]) return 1; if(vis[v]||mark[v]) continue; vis[v]=1; if(dfs(v)) return 1; } inq[u]=0; return 0; } int main(){ scanf("%d",&n); for(int i=1;i<=n;++i) Insert(); getfail(); dfs(1)?puts("TAK"):puts("NIE"); return 0; }