1. 程式人生 > >P2444 [POI2000]病毒

P2444 [POI2000]病毒

names bad 出了 程序 tree -c tom mes right

P2444 [POI2000]病毒

題目描述

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

示例:

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

任務:

請寫一個程序:

1.在文本文件WIR.IN中讀入病毒代碼;

?2.判斷是否存在一個無限長的安全代碼;

?3.將結果輸出到文件WIR.OUT中。

輸入輸出格式

輸入格式:

在文本文件WIR.IN的第一行包括一個整數n(n2000),表示病毒代碼段的數目。以下的n行每一行都包括一個非空的01字符串——就是一個病毒代碼段。所有病毒代碼段的總長度不超過30000。

輸出格式:

在文本文件WIR.OUT的第一行輸出一個單詞:

TAK——假如存在這樣的代碼;

NIE——如果不存在。

輸入輸出樣例

輸入樣例#1:
3
01 
11 
00000
輸出樣例#1:
NIE

這是一道非常有意思的AC自動機題目,首先我們可以根據讀入的模式串把trie樹給建好,然後把next數組搞出來,然後就GG了,這也沒個文本串啊,這咋辦啊?既然正著想不好辦,我們可以考慮反過來想,如果有這樣的一個字符串滿足題目的要求不含有病毒代碼,那麽把這個字符串在trie樹上跑回咋樣呢?對啦,這個文本串一定不可以與任何一個模式串成功匹配,也就是說這個文本串會在trie樹上跳來跳去,那麽我們一定可以在trie樹上找到一個環使得這個文本串會在這個環裏來回跳。

看下面這個例子:

3

011

11

00000技術分享圖片我們先把trie樹建好,把next數組構造好,然後我們把文本串010101…跑一遍AC自動機,會發生什麽呢?

技術分享圖片

不難看出,這個文本串會在上圖中的0、1兩個結點中來回跳,而且不能和任何一個模式串完全匹配。這樣問題也就轉化為了能否在trie樹上利用next數組找到一個環使得這個環上不含有模式串的結束結點。看上去我們直接把所有模式串的結束結點打上標記然後直接dfs找環不就行了?貌似沒有什麽問題,但是這樣做並不正確。

看下面的例子:

3

00

1

0100

技術分享圖片

我們繼續把trie樹和next數組給搞出來,然後進行dfs找環,但是這樣會發生什麽呢?我們會沿著上圖中藍色箭頭所指的線路走,這樣就會錯誤的判斷為這個trie樹上有環,但是顯然這個樹上是不存在環的,那麽問題出在哪裏呢?

技術分享圖片

對了,就是這個結點,根據next數組的含義,我們不難發現,只要這個結點的next鏈上含有模式串的結束結點,那麽我們也應當把這個點打上模式串的結束標記,所以,我們還要把next鏈上存在模式串結束結點的點打上標記,這樣就沒有問題了。

技術分享圖片
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cmath>
  4 #include<string>
  5 #include<cstring>
  6 #include<map>
  7 #include<queue>
  8 #include<stack>
  9 #include<algorithm>
 10 #include<vector>
 11 #define maxn 60005
 12 using namespace std;
 13 
 14 inline int read()
 15 {
 16     char c=getchar();
 17     int res=0,x=1;
 18     while(c<0||c>9)
 19     {
 20         if(c==-)
 21         x=-1;
 22         c=getchar();
 23     }
 24     while(c>=0&&c<=9)
 25     {
 26         res=res*10+(c-0);
 27         c=getchar();
 28     }
 29     return res*x;
 30 }
 31 
 32 int n,tot=1,pd;
 33 int tree[maxn][15],nt[maxn],bo[maxn],dfn[maxn],vis[maxn];
 34 char a[maxn];
 35 queue<int>q;
 36 
 37 void trie(char *s)
 38 {
 39     int len=strlen(s),u=1;
 40     for(register int i=0;i<len;i++)
 41     {
 42         int c=s[i]-0;
 43         if(!tree[u][c])
 44         {
 45             tree[u][c]=++tot;
 46         }
 47         u=tree[u][c];
 48     }
 49     bo[u]=1;
 50 }
 51 
 52 void bfs()
 53 {
 54     for(register int i=0;i<10;i++)
 55     {
 56         tree[0][i]=1;
 57     }
 58     nt[1]=0;q.push(1);
 59     while(q.size())
 60     {
 61         int u=q.front();q.pop();
 62         for(register int i=0;i<10;i++)
 63         {
 64             if(!tree[u][i])
 65             tree[u][i]=tree[nt[u]][i];
 66             else
 67             {
 68                 int v=tree[u][i];
 69                 q.push(v);
 70                 nt[v]=tree[nt[u]][i];
 71             }
 72         }
 73     }
 74 }
 75 
 76 void dfs(int x)
 77 {
 78     if(pd) return;
 79     for(int i=0;i<=1;i++)
 80     {
 81         if(!vis[tree[x][i]]&&!bo[tree[x][i]])
 82         {
 83             vis[tree[x][i]]=1;
 84             dfs(tree[x][i]);
 85             vis[tree[x][i]]=0;
 86         }
 87         else if(!bo[tree[x][i]]&&vis[tree[x][i]])
 88         {
 89             pd=1;
 90             return;
 91         }
 92     }
 93 }
 94 
 95 int main()
 96 {
 97     n=read();
 98     for(register int i=1;i<=n;i++)
 99     {
100         scanf("%s",a);
101         trie(a);
102     }
103     bfs();
104     vis[1]=1;
105     for(int i=2;i<=tot;i++)
106     {
107         int k=i,m=0;
108         while(k>1)
109         {
110             if(bo[k])
111             m=k;
112             k=nt[k];
113         }
114         k=i;
115         if(m==0) continue;
116         while(k>1)
117         {
118             if(k==m) break;
119             bo[k]=1;
120             k=nt[k];
121         }
122     }
123     dfs(1);
124     if(pd)
125     {
126         printf("TAK\n");
127     }
128     else
129     {
130         puts("NIE");
131     }
132     return 0;
133 }
View Code

P2444 [POI2000]病毒