[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;
}
原文地址:https://www.cnblogs.com/Foggy-Forest/p/13626709.html