P2444 [POI2000]病毒
AC自动机
如果有一个无限长的安全串
那么这个串在所有危险代码构成的AC自动机上一直匹配下去都不会走到结束标记
因为如果走到结束标记说明串中有危险代码
考虑怎样才能无限匹配
可以发现,如果在AC自动机上从根节点出发,一直走能走出一个环的话
那么就可以一直走这个环,从而无限匹配下去
所以要在自动机上找环
可以用DFS实现
有一点要注意
不但有结尾标记的点不能走
如果一个点一直走 fail 能走到一个有结束标记的点
那么这个点也不能走
因为这串的后面一部分会包含危险代码(AC自动机的性质)
然后就是代码了
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> using namespace std; const int N=3e4+7; int n; int c[N][2],pd[N],fail[N],cnt; char a[N]; inline void ins()//插入危险代码 { int u=0,l=strlen(a); for(int i=0;i<l;i++) { int v=a[i]-'0'; if(!c[u][v]) c[u][v]=++cnt; u=c[u][v]; } pd[u]=1; } queue <int> q; inline void pre()//预处理fail以及不能走的点 { for(int i=0;i<=1;i++) if(c[0][i]) q.push(c[0][i]); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0;i<=1;i++) { int v=c[u][i]; if(!v) c[u][i]=c[fail[u]][i]; else { fail[v]=c[fail[u]][i]; if(pd[fail[v]]) pd[v]=1; q.push(v); } } } } bool vis[N],p[N];//vis表示是否走过,p表示是否在当前路径上 inline void dfs(int x)//dfs找环 { if(p[x])//如果在当前路径上,说明找到环了 { cout<<"TAK"; exit(0); } if(vis[x]||pd[x]) return; vis[x]=p[x]=1; dfs(c[x][0]); dfs(c[x][1]); p[x]=0; } int main() { cin>>n; for(int i=1;i<=n;i++) { scanf("%s",a); ins(); } pre(); dfs(0); cout<<"NIE"; return 0; }