1. 程式人生 > >BZOJ2938: [Poi2000]病毒(AC自動機)

BZOJ2938: [Poi2000]病毒(AC自動機)

2938: [Poi2000]病毒

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 1678  Solved: 849
[Submit][Status][Discuss]

Description

二進位制病毒審查委員會最近發現瞭如下的規律:某些確定的二進位制串是病毒的程式碼。如果某段程式碼中不存在任何一段病毒程式碼,那麼我們就稱這段程式碼是安全的。現在委員會已經找出了所有的病毒程式碼段,試問,是否存在一個無限長的安全的二進位制程式碼。 示例: 例如如果{011, 11, 00000}為病毒程式碼段,那麼一個可能的無限長安全程式碼就是010101…。如果{01, 11, 000000}為病毒程式碼段,那麼就不存在一個無限長的安全程式碼。 任務: 請寫一個程式: l          讀入病毒程式碼;
l          判斷是否存在一個無限長的安全程式碼; l          將結果輸出

Input

  第一行包括一個整數n,表示病毒程式碼段的數目。以下的n行每一行都包括一個非空的01字串——就是一個病毒程式碼段。所有病毒程式碼段的總長度不超過30000。

Output

你應在在文字檔案WIN.OUT的第一行輸出一個單詞: l          TAK——假如存在這樣的程式碼;
l          NIE——如果不存在。

Sample Input

3
01
11
00000

Sample Output

NIE

 

思路:建立AC自動機,構fail指標,然後如果在避開單詞End的情況下存在環,說明可以無限下去。 tarjan求無向圖環的時候需要一個佇列維護,以免橫邊,這裡也是。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using
namespace std; const int maxn=30010; int ch[maxn][2],End[maxn],q[maxn],fail[maxn]; int cnt,Flag,head,tail,vis[maxn],inq[maxn]; char c[maxn]; void insert() { int L=strlen(c+1),Now=0; rep(i,1,L) { if(!ch[Now][c[i]-'0']) ch[Now][c[i]-'0']=++cnt; Now=ch[Now][c[i]-'0']; } End[Now]=1; } void buildfail() { rep(i,0,1) if(ch[0][i]) q[++head]=ch[0][i]; while(tail<head){ int Now=q[++tail]; rep(i,0,1){ if(ch[Now][i]){ fail[ch[Now][i]]=ch[fail[Now]][i]; q[++head]=ch[Now][i]; End[ch[Now][i]]|=End[fail[ch[Now][i]]]; } else ch[Now][i]=ch[fail[Now]][i]; } } } void dfs(int Now) { if(Flag||vis[Now]) return ; if(inq[Now]){ Flag=true; return ;} inq[Now]=1; if(!End[ch[Now][0]]) dfs(ch[Now][0]); if(!End[ch[Now][1]]) dfs(ch[Now][1]); vis[Now]=1; inq[Now]=0; } int main() { int N; scanf("%d",&N); rep(i,1,N){ scanf("%s",c+1); insert(); } buildfail(); dfs(0); if(Flag) puts("TAK"); else puts("NIE"); return 0; }