题解 lg2444[POI2000]病毒 --AC自动机
ac自动机练手好题:lg P2444 病毒
题意
有\(n\)个模式串,问是否存在无限长的文本串其中不出现模式串
思路
构建这\(n\)个模式串AC自动机,满足要求的文本串必然满足在自动机上跑不会碰到结束节点,则必然会构成一个环.于是乎解法就是在Trie图上从根节点开始遍历,禁止遍历到结束节点,若找到环则输出\(TAK\),否则为\(NIE\)
注意(自己掉的坑)
-
trie图可以直接在trie树上建,新建图还麻烦了
-
如果这个点跳\(fail\)能跳到结束节点,其本身也应该被禁止
代码
#include<bits/stdc++.h>
using namespace std;
int const N=3e5;
int n;
char s[N];
bool ans;
struct ACAutomaton{
int tot=0,_=0;
int fail[N],trie[N][2],ed[N],h[N];
bool ban[N],vis[N<<1],instack[N];
queue<int>q;
struct edge{
int to,next;
}e[N<<1];
void add(int be,int to){
e[++_].to=to,e[_].next=h[be],h[be]=_;
}
void init(){
memset(h,0,sizeof(h));
memset(trie,0,sizeof(trie));
memset(fail,0,sizeof(fail));
tot=0;
}
void insert(){
int len=strlen(s+1),now=0;
for(int i=1;i<=len;i++){
int x=s[i]-'0';
if(!trie[now][x]){trie[now][x]=++tot;}
now=trie[now][x];
}
ban[now]=1;
}
void getfail(){
for(int i=0;i<=1;i++){
if(trie[0][i]){
fail[trie[0][i]]=0;
add(trie[0][i],0);
q.push(trie[0][i]);
}
}
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<=1;i++){
add(trie[x][i],trie[fail[x]][i]);
if(trie[x][i]){
fail[trie[x][i]]=trie[fail[x]][i];
ban[trie[x][i]]|=ban[fail[trie[x][i]]];
q.push(trie[x][i]);
}else{
trie[x][i]=trie[fail[x]][i];
}
}
}
}
void dfs(int x){
//printf("%d\n",x);
if(instack[x]){printf("TAK\n");exit(0);}
if(vis[x]||ban[x])return;
vis[x]=1;instack[x]=1;
for(int i=0;i<2;i++){
dfs(trie[x][i]);
}
instack[x]=0;
}
}ACA;
int main(){
scanf("%d",&n);
ACA.init();
for(int i=1;i<=n;i++){
scanf("%s",s+1);
ACA.insert();
}
ACA.getfail();
ACA.dfs(0);
printf("NIE\n");
return 0;
}