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

洛谷 P2444 [POI2000]病毒

題目:病毒


思路:

首先把所有病毒程式碼建成trie。

然後建出fail指標,順便完成AC自動機的優化。

然後分析樣例可以獲得這樣一棵trie圖——

3
01 
11 
00000

其中黑邊是trie的正常邊,綠邊是在處理AC自動機時連上的優化邊,紅邊是fail指標。

節點內部的數是節點權值,旁邊的是節點編號。

可以知道,一個無限的文字串,在AC自動機匹配的過程中一定會“死迴圈”,即存在環狀的匹配。

這在這顆trie中的體現就是存在從根節點開始的環。

要注意,這裡的環是由綠邊和黑邊構成的,即不考慮走fail指標(想一想,為什麼)。

由於這個環狀匹配不存在病毒,所以我們要找的環中一定不存在一個病毒的結尾。

且考慮AC自動機執行的過程,要從根節點開始匹配。


程式碼:

#include<bits/stdc++.h>
using namespace std;

#define maxn 2000
#define maxm 30000
#define read(x) scanf("%d",&x)

struct Trie {
	int ch[maxm+5][2];
	int cnt,v[maxm*2+5];
	int fl[maxm*2+5];
	
	void insert(char* x) {
		int u=0,len=strlen(x);
		for(int i=0; i<
len; i++) { int y=x[i]-'0'; if(!ch[u][y]) ch[u][y]=++cnt; u=ch[u][y]; } v[u]=true; } void make_fl() { queue<int> que; if(ch[0][0]) que.push(ch[0][0]); if(ch[0][1]) que.push(ch[0][1]); while(!que.empty()) { int u=que.front(); que.pop(); for(int i=0; i<=1; i++) if
(!ch[u][i]) ch[u][i]=ch[fl[u]][i]; else { que.push(ch[u][i]); fl[ch[u][i]]=ch[fl[u]][i]; if(v[fl[ch[u][i]]]) v[ch[u][i]]=true; } } } } ac ; int n; bool vis[maxm*2+5],use[maxm*2+5]; bool dfs(int x) { vis[x]=true; for(int i=0;i<=1;i++) { int y=ac.ch[x][i]; if(!y) continue; if(vis[y]) return true; if(use[y]||ac.v[y]) continue; use[y]=true; if(dfs(y)) return true; } vis[x]=false; return false; } int main() { read(n); for(int i=1; i<=n; i++) { char x[maxn+5]; scanf("%s",x); ac.insert(x); } ac.make_fl(); bool ans=dfs(0); if(ans) printf("TAK"); else printf("NIE"); return 0; }