P2444 [POI2000]病毒
AC自动机(模板题)
一个重要的性质(同样适用于KMP):一个串末尾节点的fail数组指向的子串一定是这个串的后缀
(我真的不想再忘了)
我们的目标是在AC自动机的图中找到一个环,里面没有任何病毒标记
和以往的AC自动机不同,我们在匹配的过程中要避开病毒标记,可以用dfs实现
用ins数组存当前路径,vis数组标记哪些点已被访问过
end.
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; struct data{ int fail,nxt[2],end; }a[30002]; int n,cnt; char q[30002]; bool vis[30002],ins[30002],ok; inline void Trie_build(){ scanf("%s",q); int u=0,len=strlen(q); for(int i=0;i<len;++i){ int p=q[i]-'0'; if(!a[u].nxt[p]) a[u].nxt[p]=++cnt; u=a[u].nxt[p]; }++a[u].end; } inline void AC_build(){ //普通的AC自动机核心 queue <int> h; for(int i=0;i<2;++i) if(a[0].nxt[i]) h.push(a[0].nxt[i]); while(!h.empty()){ int x=h.front(); h.pop(); for(int i=0;i<2;++i){ if(a[x].nxt[i]){ a[a[x].nxt[i]].fail=a[a[x].fail].nxt[i]; h.push(a[x].nxt[i]); if(a[a[a[x].nxt[i]].fail].end) a[a[x].nxt[i]].end=1; //如果这个串的后缀是病毒那么它也一定是病毒 }else a[x].nxt[i]=a[a[x].fail].nxt[i]; //优化 } } } inline void dfs(int x){ if(ok) return ; ins[x]=vis[x]=1; for(int i=0;i<2;++i){ int to=a[x].nxt[i]; if(ins[to]) {ok=1; break;} if(a[to].end||vis[to]) continue; dfs(to); }ins[x]=0; } int main(){ scanf("%d",&n); for(int i=1;i<=n;++i) Trie_build(); AC_build(); dfs(0); if(ok) printf("TAK"); else printf("NIE"); return 0; }