您的位置:首页 > 其它

【BZOJ2938】病毒,AC自动机练习

2016-04-04 20:33 288 查看
传送门(权限题)

2938: [Poi2000]病毒

Time Limit: 1 Sec Memory Limit: 128 MB

Submit: 462 Solved: 240

[Submit][Status][Discuss]

Description

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

示例:

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

任务:

请写一个程序:

l 读入病毒代码;

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

l 将结果输出

Input

第一行包括一个整数n,表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。

Output

你应在在文本文件WIN.OUT的第一行输出一个单词:

l TAK——假如存在这样的代码;

l NIE——如果不存在。

Sample Input

3

01

11

00000

Sample Output

NIE

写在前面:给了三个样例的良心题

思路:

我们首先确定,如果可能,那么符合条件的字符串中一定有一个循环的字符串,如果不可能,那一定没有循环的字符串(因为循环的字符串是有循环节的,如果没有循环的字符串,那就意味着所有的循环节都不可以,那就是没有符合条件的了,反之亦如此)

然后偷个懒

zky:

首先我们把所有串建一个AC自动机

方便起见我们直接把fail指针合并到子结点

如果一个串能无限长,也就是说它可以在AC自动机上一直进行匹配但就是匹配不上

也就是说匹配指针不能走到val为1的结点,设这个点为x

即root..x是一个病毒串

那么fail指针指向x的y也不能走

因为root..x是root..y的一个后缀

处理出来判断有向图是否有环

dfs即可

建fail时把那些当前节点i没有的字符(相当于失配状态)都转移到fail[i]上去,之后我们只要从根上出发dfs一通乱走,如果途中遇到字符串结束的标记节点,就不走(因为一走不就相当于有这个病毒了吗),如果最后无路可走(怎么选都要经过标记节点)那就NiE了,但如果我们能走出一个没有标记节点的环,那就说明我们可以找到一个循环节使字符串合法且无限延长下去了

注意:

DFS时注意及时退出,减少搜索树深度,用两个bool数组记录节点的搜索状态,其中一个表示这个点是否曾经搜索过,减少重复搜索;另一个表示这个点是否在我们目前想要的环的路径上(可能有点抽象,就是说我们是否已经经过这个点并且走在这个点所连接的边上,搜索完是要回溯的,而前一个bool数组只是单纯记录这个点是否走过,不必回溯更改)

代码:

#include<bits/stdc++.h>
using namespace std;
int root=1,tot=1,n;
int trie[30002][2],fail[30002];
bool num[30002],vis[30002],flag[30002];
char s[30002];
queue<int>q;
void insert(char s[])
{
int len=strlen(s),now=root;
for (int i=0;i<len;i++)
{
if (!trie[now][s[i]-'0']) trie[now][s[i]-'0']=++tot;
now=trie[now][s[i]-'0'];
}
num[now]=1;
}
void build()
{
int now,tmp;
q.push(root);
while (!q.empty())
{
now=q.front();
q.pop();
for (int i=0;i<2;i++)
if (trie[now][i])
{
tmp=fail[now];
while (tmp&&!trie[tmp][i]) tmp=fail[tmp];
if (tmp&&now!=root)
fail[trie[now][i]]=trie[tmp][i],
num[trie[now][i]]+=num[trie[tmp][i]];
else fail[trie[now][i]]=root;
q.push(trie[now][i]);
}
else
{
if (now==root) trie[now][i]=root;
else trie[now][i]=trie[fail[now]][i];
}
}
}
void dfs(int x)
{
if (flag[x]) {printf("TAK");exit(0);}
if (num[x]||vis[x]) return;
if (x!=root)vis[x]=1;
flag[x]=1;
for (int i=0;i<2;i++)
dfs(trie[x][i]);
flag[x]=0;
}
main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%s",s),
insert(s);
build();
dfs(root);
printf("NIE");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: