洛谷P2444:病毒——AC自动机

洛谷P2444

P2444 [POI 2000] 病毒

题目描述

二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。

示例:

例如如果 \(\{011, 11, 00000\}\) 为病毒代码段,那么一个可能的无限长安全代码就是 \(010101 \ldots\)。如果 \(\{01, 11, 000000\}\) 为病毒代码段,那么就不存在一个无限长的安全代码。

现在给出所有的病毒代码段,判断是否存在无限长的安全代码。

输入格式

第一行包括一个整数 \(n\),表示病毒代码段的数目。

以下的 \(n\) 行每一行都包括一个非空的 \(01\) 字符串,代表一个病毒代码段。

输出格式

如果存在无限长的安全代码,输出 TAK,否则输出 NIE

输入输出样例 #1

输入 #1

3
01 
11 
00000

输出 #1

NIE

说明/提示

\(1 \leq n \leq 2000\),所有病毒代码段的总长度不超过 \(3 \times 10^4\)

思路

其实不算特别难(凡是我能一下子看懂题解的题都不算难),运用逆向思维,我们考虑一个满足题意的字符串s拿到Trie树上匹配会怎样?

\(A:\)一直不会碰到危险节点

赤裸裸地指向了一个东西——环 .
且该环要满足:1.不经过危险节点 2.从根节点到环要至少存在一条不经过危险节点的路径

所以我们从根节点开始dfs,遇到危险节点就避开,看能否走回到一个之前标记的可走的点,即形成一个环.代码真的很好理解QwQ

注意:一定要记忆化!!!不然...... rt:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define lid (id<<1)
#define rid (id<<1|1)
const int N=1e5+10;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}return x*f;}inline void write(int x){if(x<0)x*=-1,putchar('-');if(x>9)write(x/10);putchar(x%10+'0');return;}inline int max(int x,int y){return (x<y)?y:x;}inline int min(int x,int y){return (x<y)?x:y;}

int n,tot;
int tr[N][2],fail[N];
int vis[N];
char s[N];

inline void build(char* t)
{
	int l=strlen(t),rt=0;
	for(int i=0;i<l;i++)
	{
		int p=t[i]-'0';
		if(!tr[rt][p]) tr[rt][p]=++tot;
		rt=tr[rt][p];
	}
	vis[rt]=1;
}

inline void getfail()
{
	queue<int> q;
	for(int i=0;i<2;i++) if(tr[0][i]) q.push(tr[0][i]);
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=0;i<2;i++)
		{
			if(tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]);
			else tr[u][i]=tr[fail[u]][i];
			if(vis[fail[tr[u][i]]]) vis[tr[u][i]]=1;//如果失配指针指向危险节点,则该节点也为危险节点 
		}
	}
}

int ins[N],used[N];
bool dfs(int x)
{
	ins[x]=1;
	for(int i=0;i<2;i++)
	{
		int v=tr[x][i];
		if(ins[v])return 1;//走回到某个可走的点,形成环 
		if(vis[v]) continue;//危险节点,此路不通 
		if(used[v])continue;//记忆化,优化时间复杂度 
		used[v]=1;
		if(dfs(v))return 1;//接着往下找 
	}
	ins[x]=0;
	return 0;
}

signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		cin>>s;
		build(s);
	}
	getfail();
	if(dfs(0)) cout<<"TAK";
	else cout<<"NIE";
	return 0;
}

欢迎指出问题或错误,点个推荐再走啦(卖萌)

posted @ 2025-05-12 10:52  Crab2016  阅读(13)  评论(0)    收藏  举报