洛谷2444 [POI2000]病毒(AC自動機)(DFS)
阿新 • • 發佈:2018-11-10
題目
二進位制病毒審查委員會最近發現瞭如下的規律:某些確定的二進位制串是病毒的程式碼。如果某段程式碼中不存在任何一段病毒程式碼,那麼我們就稱這段程式碼是安全的。現在委員會已經找出了所有的病毒程式碼段,試問,是否存在一個無限長的安全的二進位制程式碼。
示例:
例如如果{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; }