洛谷 P2444 [POI2000]病毒 解题报告

P2444 [POI2000]病毒

题目描述

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

示例:

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

任务:

请写一个程序:

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

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

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

输入输出格式

输入格式:

在文本文件WIR.IN的第一行包括一个整数n(n\le 2000)(n≤2000),表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。

输出格式:

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

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

NIE——如果不存在。


越想越乱我是得多菜啊

多串匹配,我们选择AC自动机,要求不能到达单词末尾且无限长度

等价于在tire图上寻找经过根的环

\(fail\)指针的时候可以多把末尾打一些标记以剪枝

寻找环用tarjan


Code:

#include <cstdio>
#include <cstring>
const int N=3e5+10;
int ch[N][2],is[N],fail[N],tot,n,q[N],l,r,ans=1;
char c[N];
void init()
{
    scanf("%s",c+1);
    int len=strlen(c+1),now=0;
    for(int i=1;i<=strlen(c+1);i++)
    {
        if(!ch[now][c[i]-'0']) ch[now][c[i]-'0']=++tot;
        now=ch[now][c[i]-'0'];
    }
    is[now]=1;
}
void build()
{
    if(ch[0][0]) q[r]=ch[0][0];
    if(ch[0][1]) q[++r]=ch[0][1];
    while(l<=r)
    {
        int now=q[l++];
        for(int i=0;i<=1;i++)
        {
            if(ch[now][i])
            {
                fail[ch[now][i]]=ch[fail[now]][i];
                is[ch[now][i]]|=is[ch[fail[now]][i]];
                q[++r]=ch[now][i];
            }
            else ch[now][i]=ch[fail[now]][i];
        }
    }
}
int in[N],used[N],flag=0;
void dfs(int now)
{
    in[now]=used[now]=1;
    for(int i=0;i<=1;i++)
    {
        int v=ch[now][i];
        if(!used[v]&&!is[v]) dfs(v);
        else if(in[v]) flag=1;
    }
    in[now]=0;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) init();
    build();
    dfs(0);
    if(!flag) printf("NIE\n");
    else printf("TAK\n");
    return 0;
}


2018.8.28

posted @ 2018-08-28 20:24  露迭月  阅读(274)  评论(0编辑  收藏  举报