bzoj3940 Censoring

题目链接

solution

将所有的单词建出AC自动机。然后用母串在上面走,并且记录下在母串中的每个位置对应到AC自动机上的位置。当走到一个单词的结尾时,就回到这个单词长度之前的位置。

问题在于如何找到这个单词长度之前的位置。我们用个栈来记录答案,每找到一个长度为len的单词,就从栈顶弹出len个位置。显然每个位置最多被弹一遍。所以总复杂度就是\(O(n)\)的。

code

/*
* @Author: wxyww
* @Date:   2020-04-19 08:11:00
* @Last Modified time: 2020-04-19 08:18:54
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
const int N = 100010;
ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1; c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0'; c = getchar();
	}
	return x * f;
}
int trie[N][27],fail[N],tot,bz[N];

char s[N],t[N];
void insert() {
	int n = strlen(t + 1);
	int now = 0;
	for(int i = 1;i <= n;++i) {
		int x = t[i] - 'a';
		if(!trie[now][x]) trie[now][x] = ++tot;
		now = trie[now][x];
	}
	bz[now] = n;
	// cout<<now<<endl;
}
queue<int>q;
void build() {
	for(int i = 0;i < 26;++i) if(trie[0][i]) q.push(trie[0][i]);

	while(!q.empty()) {
		int u = q.front();q.pop();
		for(int i = 0;i < 26;++i) {
			if(!trie[u][i]) trie[u][i] = trie[fail[u]][i];
			else fail[trie[u][i]] = trie[fail[u]][i],q.push(trie[u][i]);
		}
	}
}
int ans[N],dy[N];
int main() {
	scanf("%s",s + 1);
	int n = read();
	for(int i = 1;i <= n;++i) {
		scanf("%s",t + 1);
		insert();
	}
	build();
	int top = 0;
	int m = strlen(s + 1);
	int now = 0;
	// cout<<tot<<endl;

	for(int i = 1;i <= m;++i) {
		now = trie[now][s[i] - 'a'];
		dy[i] = now;
		ans[++top] = i;
		if(bz[now]) {

			int x = bz[now];
			top -= x;
			now = dy[ans[top]];
		}
	}
	for(int i = 1;i <= top;++i) putchar(s[ans[i]]);

	return 0;
}
posted @ 2020-04-19 08:28  wxyww  阅读(114)  评论(0编辑  收藏  举报