题目大意
FJ把杂志上所有的文章摘抄了下来并把它变成了一个长度不超过10^5的字符串S。他有一个包含n个单词的列表,列表里的n个单词记为t1...tN。他希望从S中删除这些单词。
FJ每次在S中找到最早出现的列表中的单词(最早出现指该单词的开始位置最小),然后从S中删除这个单词。他重复这个操作直到S中没有列表里的单词为止。注意删除一个单词后可能会导致S中出现另一个列表中的单词
FJ注意到列表中的单词不会出现一个单词是另一个单词子串的情况,这意味着每个列表中的单词在S中出现的开始位置是互不相同的
请帮助FJ完成这些操作并输出最后的S
题目分析
我们通过观察,容易得出我们要做的是多模式串匹配,考虑使用AC自动机。
用两个栈分别记录扫到当前字符x,一个记录当前栈内的所有字符,另一个对应记录这些字符在AC自动机上的位置(节点号)。
当我们走到一个可行节点时,把两个栈都弹出 当前节点表示的模式串的长度 个元素,相当于题目中的删除操作。这样一边下来即可得到答案。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=1e5+10; 4 5 char s[MAXN],word[MAXN]; 6 int n,top; 7 int sta[MAXN],sgn[MAXN]; 8 struct AC_Automation{ 9 int tot; 10 int Trie[MAXN][27],fail[MAXN],num[MAXN]; 11 12 inline void Insert(char *s){ 13 int p=0,len=strlen(s); 14 for(int i=0,x;i<len;++i){ 15 x=s[i]-‘a‘; 16 if(!Trie[p][x]) Trie[p][x]=++tot; 17 p=Trie[p][x]; 18 } 19 num[p]=len; 20 } 21 inline void Make_Fail(){ 22 queue<int> q; 23 for(int i=0;i<26;++i) 24 if(Trie[0][i]) 25 q.push(Trie[0][i]); 26 while(!q.empty()){ 27 int x=q.front();q.pop(); 28 for(int i=0;i<26;++i){ 29 if(!Trie[x][i]){ 30 Trie[x][i]=Trie[fail[x]][i]; 31 continue; 32 } 33 fail[Trie[x][i]]=Trie[fail[x]][i]; 34 q.push(Trie[x][i]); 35 } 36 } 37 } 38 inline void Solve(char *s){ 39 int p=0,x,len=strlen(s),i=0; 40 while(i<len){ 41 x=s[i]-‘a‘; 42 p=Trie[p][x]; 43 sgn[++top]=p; 44 sta[top]=i; 45 if(num[p]){ 46 top-=num[p]; 47 if(!top) p=0; 48 else p=sgn[top]; 49 } 50 ++i; 51 } 52 53 } 54 }AC; 55 56 int main(){ 57 scanf("%s%d",s,&n); 58 for(int i=1;i<=n;++i){ 59 scanf("%s",word); 60 AC.Insert(word); 61 } 62 AC.Make_Fail(); 63 AC.Solve(s); 64 for(int i=1;i<=top;++i) 65 printf("%c",s[sta[i]]); 66 return 0; 67 }
原文地址:https://www.cnblogs.com/LI-dox/p/11214056.html
时间: 2024-10-11 20:59:04