[bzoj2938][Poi2000]病毒——AC自动机

题目大意:

给定n个01串,求是否存在一个长度无穷的01串使得这个01串不包含任何一个给定的串。

思路:

考虑AC自动机匹配的过程是在Trie树上不停地跳,那么如果我们可以找到一个串使得这个串可以一直在Trie上跳并且永远跳不到匹配节点就说明可行。
可以发现这样的话这个串在AC自动机上的匹配一定是会出现环的,于是我们直接dfs找环就好了。
考虑如何判断这个节点是否可以走到,即这个节点以及这个节点的所有fail都没有匹配才可以走,在求fail的时候可以预处理出来。
dfs找环的时候记得要开两个bool数组,一个标记是否来过,一个标记是否在栈中。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
	freopen("bzoj2398.in","r",stdin);
	freopen("bzoj2398.out","w",stdout);
}

template<typename T>void read(T &_){
	_=0; T f=1; char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
	_*=f;
}

const int maxn=6e4+10;
int n;
int ans[maxn],tp;

struct AC_Automaton{
	int ch[maxn][2],num[maxn],fail[maxn],cnt;
	bool vis[maxn],in[maxn];
	void insert(char *s){
		int len=strlen(s+1),u=0,c;
		REP(i,1,len){
			c=s[i]-'0';
			if(!ch[u][c])ch[u][c]=++cnt;
			u=ch[u][c];
		}
		++num[u];
	}
	void build(){
		int h=1,t=0,q[maxn];
		REP(i,0,1)if(ch[0][i])
			q[++t]=ch[0][i];
		while(h<=t){
			int u=q[h++];
			REP(i,0,1){
				int v=ch[u][i];
				if(v)q[++t]=v;
				if(v){
					fail[v]=ch[fail[u]][i];
					num[v]|=num[fail[v]];
				}
				else ch[u][i]=ch[fail[u]][i];
			}
		}
	}
	bool dfs(int u){
		if(num[u])return false;

		if(in[u])return true;
		if(vis[u])return false;

		vis[u]=in[u]=1;
		if(dfs(ch[u][0]) || dfs(ch[u][1]))return true;

		in[u]=0;
		return false;
	}
}T;

int main(){
	File();
	char s[maxn];
	read(n);
	REP(i,1,n){
		scanf("%s",s+1);
		T.insert(s);
	}

	T.build();

	printf("%s
",T.dfs(0) ? "TAK" : "NIE");

	REP(i,1,tp)printf("%d",ans[i]);
	return 0;
}

原文地址:https://www.cnblogs.com/ylsoi/p/10181266.html