BZOJ_3940_[Usaco2015 Feb]Censoring_AC自动机

BZOJ_3940_[Usaco2015 Feb]Censoring_AC自动机

Description

FJ把杂志上所有的文章摘抄了下来并把它变成了一个长度不超过10^5的字符串S。他有一个包含n个单词的列表,列表里的n个单词
记为t_1...t_N。他希望从S中删除这些单词。 
FJ每次在S中找到最早出现的列表中的单词(最早出现指该单词的开始位置最小),然后从S中删除这个单词。他重复这个操作直到S中
没有列表里的单词为止。注意删除一个单词后可能会导致S中出现另一个列表中的单词 
FJ注意到列表中的单词不会出现一个单词是另一个单词子串的情况,这意味着每个列表中的单词在S中出现的开始位置是互不相同的 
请帮助FJ完成这些操作并输出最后的S

Input

第一行包含一个字符串S 
第二行包含一个整数N 
接下来的N行,每行包含一个字符串,第i行的字符串是t_i

Output

The string S after all deletions are complete. It is guaranteed that S will not become empty during the deletion process.
一行,输出操作后的S
 

Sample Input

begintheescapexecutionatthebreakofdawn
2
escape
execution

Sample Output

beginthatthebreakofdawn

首先是要对单词建立AC自动机。
然后用一个栈记录答案和匹配到哪个点。
因为题里说不会有一个单词会是另一个单词的子串,于是遇到被标记的结点就弹栈即可。
 
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 200050
int ch[N][26],cnt=1,fail[N],flg[N],n,S[N],Q[N],l,r,len[N],ans[N];
char w[N],s[N];
void insert(int x) {
	int p=1,i;
	for(i=1;w[i];i++) {
		int &k=ch[p][w[i]-'a'];
		if(!k) k=++cnt;
		p=k;
	}
	flg[p]=x;
}
void build() {
	int i,p;
	for(i=0;i<26;i++) ch[0][i]=1;
	Q[r++]=1;
	while(l<r) {
		p=Q[l++];
		for(i=0;i<26;i++) {
			if(ch[p][i]) fail[ch[p][i]]=ch[fail[p]][i],Q[r++]=ch[p][i];
			else ch[p][i]=ch[fail[p]][i];
		}
	}
}
int main() {
	scanf("%s%d",s+1,&n);
	int ls=strlen(s+1);
	int i;
	for(i=1;i<=n;i++) {
		scanf("%s",w+1); insert(i); len[i]=strlen(w+1);
	}
	build();
	// puts("FUCK");
	int p=1;
	for(i=1;i<=ls;i++) {
		p=ch[p][s[i]-'a'];
		S[++S[0]]=p; ans[S[0]]=s[i];
		if(flg[p]) S[0]-=len[flg[p]],p=S[S[0]];
	}
	for(i=1;i<=S[0];i++) {
		printf("%c",ans[i]);
	}
}

猜你喜欢

转载自www.cnblogs.com/suika/p/9079130.html