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

来源:互联网 发布:姚明体测数据 编辑:程序博客网 时间:2024/06/05 18:39

传送门(权限题)


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");}
0 0