1. 程式人生 > >洛谷 P2444 [POI2000]病毒 解題報告

洛谷 P2444 [POI2000]病毒 解題報告

一個 超過 flag ring 這樣的 sed out 匹配 const

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

洛谷 P2444 [POI2000]病毒 解題報告