1. 程式人生 > >洛谷2444 [POI2000]病毒(AC自動機)(DFS)

洛谷2444 [POI2000]病毒(AC自動機)(DFS)

題目

二進位制病毒審查委員會最近發現瞭如下的規律:某些確定的二進位制串是病毒的程式碼。如果某段程式碼中不存在任何一段病毒程式碼,那麼我們就稱這段程式碼是安全的。現在委員會已經找出了所有的病毒程式碼段,試問,是否存在一個無限長的安全的二進位制程式碼。

示例:
例如如果{011, 11, 00000}為病毒程式碼段,那麼一個可能的無限長安全程式碼就是010101…。如果{01, 11, 000000}為病毒程式碼段,那麼就不存在一個無限長的安全程式碼。

任務:
請寫一個程式:
1.在文字檔案WIR.IN中讀入病毒程式碼;
2.判斷是否存在一個無限長的安全程式碼;
3.將結果輸出到檔案WIR.OUT中。

題解

AC自動機+DFS判環
給病毒程式碼建字典樹,並建立fail指標。
要求一個無限長的安全程式碼實際上就是要在AC自動機上找到一個不包含結尾標記的環,當然,如果一個fail指標指向的節點有結尾標記,那麼這個點也不能選,因為到了這個節點相當於到了那個節點,也就是說它的字尾是病毒程式碼。
為了方便操作,我們可以把沒有孩子的尾節點直接接上fail。
接下來就是一個dfs判斷是否有環,注意不能走有標記的節點。

程式碼

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2010,maxl=30010;
int n;
char s[maxl];

struct tree{int fail,son[2];bool c;}tr[maxl];int tot=1,root=1;

void insert()
{
    int x=root;
    int len=strlen(s+1);
    for(int i=1;i<=len;i++)
    {
        int k=s[i]-'0';
        if(!tr[x].son[k]) tr[x].son[k]=++tot;
        x=tr[x].son[k];
    }
    tr[x].c=true;
}

int head,tail,q[maxl];
void makefail()
{
    head=0,tail=1;q[0]=root;
    while(head<tail)
    {
        int x=q[head++];
        for(int k=0;k<=1;k++)
        {
            int y=tr[x].son[k];
            if(!y)
            {
                tr[x].son[k]=tr[tr[x].fail].son[k];
                continue;
            }
            int p=tr[x].fail;
            while(!tr[p].son[k]) p=tr[p].fail;
            tr[y].fail=tr[p].son[k];
            tr[y].c|=tr[tr[y].fail].c;
            q[tail++]=y;
        }
    }
}

bool vis[maxl];
void dfs(int x)
{
    vis[x]=true;
    for(int k=0;k<=1;k++)
    {
        int y=tr[x].son[k];
        if(vis[y]==true)
        {
            puts("TAK");
            exit(0);
        }
        if(y && !tr[y].c) dfs(y);
    }
    vis[x]=false;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        insert();
    }
    for(int i=0;i<=1;i++) tr[0].son[i]=1;
    makefail();
    dfs(root);
    puts("NIE");
    return 0;
}