BZOJ2938 [Poi2000]病毒 解题报告【AC自动机】【DFS】

来源:互联网 发布:js解析json方式 编辑:程序博客网 时间:2024/05/22 02:21

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
HINT
解题报告
这道题的题意是找一个无限长的串让他可以在AC自动机上无限的走下去,也就是永远失配。
这样就形成了一个有向图,现在要从根节点出发找一个环,使得路径上不经过单词结尾节点,DFS就是了。

#include<cstdio>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int N=1000000;int ch[N+5][3],sz=0,val[N+5];void insert(char s[]){    int p = 0;    int l = strlen(s);    for(int i = 0;i < l;i ++)    {        int c = s[i] - '0';        if(!ch[p][c]) ch[p][c] = ++ sz;        p = ch[p][c];    }    val[p] ++;}int fail[N+5];queue<int> q;void build_ac(){    fail[0] = 0;    for(int i = 0;i < 2;i ++)    {        int u = ch[0][i];        if(u) { q.push(u); fail[u] = 0; }    }    while(q.size())    {        int f = q.front(); q.pop();        for(int i = 0;i < 2;i ++)        {            int u = ch[f][i];            if(!u) { ch[f][i] = ch[fail[f]][i]; continue; }            q.push(u);            int v = fail[f];            while(v && !ch[v][i]) v = fail[v];            fail[u] = ch[v][i];            val[u] |= val[fail[u]];        }    }}bool ins[N+5],vis[N+5];bool dfs(int u){    ins[u] = 1;    for(int i = 0;i < 2;i ++)    {        int v = ch[u][i];        if(ins[v]) return true;        if(val[v] || vis[v]) continue;        vis[v] = 1;        if(dfs(v)) return true;    }    ins[u] = 0;    return false;}char s[N+5];int main(){    int n;    scanf("%d",&n);    for(int i = 1;i <= n;i ++)    {           scanf("%s",s);        insert(s);     }    build_ac();    if(dfs(0)) puts("TAK");    else puts("NIE");    return 0;}
原创粉丝点击