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
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]); } }