题面
看到这种匹配题总会想到 (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;
}