Loading

题解 lg2444[POI2000]病毒 --AC自动机

ac自动机练手好题:lg P2444 病毒

题意

\(n\)个模式串,问是否存在无限长的文本串其中不出现模式串

思路

构建这\(n\)个模式串AC自动机,满足要求的文本串必然满足在自动机上跑不会碰到结束节点,则必然会构成一个环.于是乎解法就是在Trie图上从根节点开始遍历,禁止遍历到结束节点,若找到环则输出\(TAK\),否则为\(NIE\)

注意(自己掉的坑)

  1. trie图可以直接在trie树上建,新建图还麻烦了

  2. 如果这个点跳\(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;
}

posted @ 2020-10-27 15:15  fpjo  阅读(66)  评论(0编辑  收藏  举报