P2444 [POI2000]病毒
P2444 [POI2000]病毒
題目描述
二進制病毒審查委員會最近發現了如下的規律:某些確定的二進制串是病毒的代碼。如果某段代碼中不存在任何一段病毒代碼,那麽我們就稱這段代碼是安全的。現在委員會已經找出了所有的病毒代碼段,試問,是否存在一個無限長的安全的二進制代碼。
示例:
例如如果{011, 11, 00000}為病毒代碼段,那麽一個可能的無限長安全代碼就是010101…。如果{01, 11, 000000}為病毒代碼段,那麽就不存在一個無限長的安全代碼。
任務:
請寫一個程序:
1.在文本文件WIR.IN中讀入病毒代碼;
?2.判斷是否存在一個無限長的安全代碼;
?3.將結果輸出到文件WIR.OUT中。
輸入輸出格式
輸入格式:
在文本文件WIR.IN的第一行包括一個整數n(n≤2000),表示病毒代碼段的數目。以下的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]病毒