【BZOJ2938】【luoguP2444】病毒

description

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

示例:

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

任务:

请写一个程序:

1.在文本文件WIR.IN中读入病毒代码;

2.判断是否存在一个无限长的安全代码;

3.将结果输出到文件WIR.OUT中。


analysis

  • 对所有模式串建\(AC\)自动机,然后考虑可行的安全代码在\(AC\)自动机上的匹配

  • 安全代码若无限长,则必然在\(AC\)自动机上无限匹配、且不会匹配到失败节点

  • 注意一个节点的\(fail\)是失败节点,该节点也是,因为\(fail\)的字符串是该点字符串的后缀

  • 然后在自动机上\(dfs\)找出一个环,节点经过两次则有解


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#define MAXN 100005
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)

using namespace std;

ll trie[MAXN][2],fail[MAXN],vis[MAXN];
bool bz[MAXN],flag[MAXN];
char s[30005];
ll n,tot,root;
queue<ll>q;

inline ll newnode(){++tot,trie[tot][0]=trie[tot][1]=-1;return tot;}
inline void init(){tot=0,root=newnode();}
inline void insert(char s[])
{
	ll len=strlen(s),now=root;
	fo(i,0,len-1)
	{
		if (trie[now][s[i]-'0']==-1)trie[now][s[i]-'0']=newnode();
		now=trie[now][s[i]-'0'];
	}
	flag[now]=1;
}
inline void buildfail()
{
	q.push(root),fail[root]=root;
	while (!q.empty())
	{
		ll now=q.front();q.pop();
		fo(i,0,1)if (trie[now][i]==-1)trie[now][i]=(now==root?root:trie[fail[now]][i]);
		else flag[trie[now][i]]|=flag[trie[fail[now]][i]],fail[trie[now][i]]=(now==root?root:trie[fail[now]][i]),q.push(trie[now][i]);
	}
}
inline bool dfs(ll x)
{
	if (vis[x]==1)return 1;
	if (vis[x]==-1)return 0;vis[x]=1;
	fo(i,0,1)if (!flag[trie[x][i]]){if (dfs(trie[x][i]))return 1;}
	vis[x]=-1;return 0;
}
int main()
{
	//freopen("P2444.in","r",stdin);
	scanf("%lld",&n),init();
	fo(i,1,n)scanf("%s",&s),insert(s);
	buildfail(),printf(dfs(root)?"TAK\n":"NIE\n");
	return 0;
}
posted @ 2019-11-08 19:47  路人黑的纸巾  阅读(177)  评论(0编辑  收藏  举报