1. 程式人生 > 實用技巧 >[USACO15FEB][AC自動機][棧] Censoring G

[USACO15FEB][AC自動機][棧] Censoring G

題面

看到這種匹配題總會想到 \(AC\) 自動機 (

實際我們匹配的時候只需要多加兩個棧:一個用於記錄下一個匹配的位置,一個用於記錄答案,分別記為 \(S_{tmp}\)\(S_{ans}\)
\(S_{tmp}\) 棧頂每加入字串上的一個位置,就將其記錄到 \(S_{ans}\) 中;
如果字串上匹配到了一個子串,兩個棧就都就跳轉到這個子串開頭的位置。
這樣就可以完成匹配了,最後輸出 \(S_{ans}\) 即可。

程式碼

# include <iostream>
# include <cstdio>
# include <string>
# include <queue>
# define MAXN 1000005

int trie[MAXN][26], cntT;
int isEnd[MAXN], fail[MAXN];
int ansS[MAXN], tmpS[MAXN], topS;

void Insert(std::string s){
	int now = 0, len = s.length();

	for(int i = 0, ch; i < len; i++){
		ch = s[i] - 'a';

		if(!trie[now][ch]){
			trie[now][ch] = ++cntT;
		}

		now = trie[now][ch];
	}

	isEnd[now] = len;
}

void InitFail(){
	std::queue<int>Q;

	for(int i = 0; i < 26; i++){
		if(trie[0][i]){
			fail[trie[0][i]] = 0;
			Q.push(trie[0][i]);
		}
	}

	while(Q.size()){
		int now = Q.front(); Q.pop();

		for(int i = 0; i < 26; i++){
			if(trie[now][i]){
				fail[trie[now][i]] = trie[fail[now]][i];
				Q.push(trie[now][i]);
			}
			else{
				trie[now][i] = trie[fail[now]][i];
			}
		}
	}
}

void Query(std::string s){
	int now = 0, len = s.length();

	for(int i = 0, ch; i < len; i++){
		ch = s[i] - 'a';
		now = trie[now][ch];

		tmpS[++topS] = now;
		ansS[topS] = i; 

		if(isEnd[now]){
			topS -= isEnd[now]; // 查到一個單詞直接跳到單詞開頭重新匹配
			if(!topS){
				now = 0;
			}
			else{
				now = tmpS[topS];
			}
		}
	}

}

int main(){
	std::string s, t;
	int n;

	std::cin>>s>>n;

	for(int i = 1; i <= n; i++){
		std::cin>>t;
		Insert(t);
	}

	InitFail();

	Query(s);

	for(int i = 1; i <= topS; i++){
		std::cout<<s[ansS[i]];
	}

	return 0;
}