还是不很理解ac自动机的内涵,所以这道题卡住了,看了题解后恍然大悟。
因为是字典树匹配,接着用两个栈,一个存字符串,另外一个存节点。
这有什么用呢?原因是当找到一个单词的时候,就可以模拟删除这个单词,也就是从栈顶取出这个单词的所有字母跟节点序号。那么现在节点栈最顶端就是删除的这个单词的前一个字母的节点标号,再从这进行下一轮的匹配,当走完所有的字母就会发现字母栈中存储的正好就是删除剩下来的字母。
#include <iostream>
#include<cstring>
using namespace std;
const int MAX_N = 1e5+10;
const int MAX_C = 26;
int a;
struct AC_Automaton{
int ch[MAX_N][MAX_C],fail[MAX_N],cnt[MAX_N];// ch 和 cnt 数组与 Trie 树中的一样;fail 保存的是失败指针。ch 和 fail 默认都为 -1
int tot; // Trie 树的总结点(不含根结点)个数
int top;
int stk[MAX_N];
void init(){
memset(ch,-1,sizeof(ch));
memset(fail,0,sizeof(fail));
tot=top=0;
memset(cnt,0,sizeof(cnt));
}
void insert(char *str){
int p=0;
for(int i=0;str[i];i++){
if(ch[p][str[i]-'a']==-1){
ch[p][str[i]-'a']=++tot;
}
p=ch[p][str[i]-'a'];
}
cnt[p]=strlen(str);
}
void build(){
int l=0,r=0,Q[MAX_N];
for(int i=0;i<MAX_C;i++){
if(ch[0][i]==-1){
ch[0][i]=0;
}else{
Q[r++]=ch[0][i];
}
}
while(l<r){
int p=Q[l++];
for(int i=0;i<MAX_C;i++){
if(ch[p][i]==-1){
ch[p][i]=ch[fail[p]][i];
}else{
fail[ch[p][i]]=ch[fail[p]][i];
Q[r++]=ch[p][i];
}
}
}
}
int count(char *str){
int p=0;
for(int i=0;str[i];i++){
p=ch[p][str[i]-'a'];
str[a++]=str[i];
stk[top++]=p;
if(cnt[p]){
a-=cnt[p];
top-=cnt[p];
p=top?stk[top-1]:0;
}
}
}
}ac;
char s[100006];
char ss[100006];
int main() {
int n;
ac.init();
scanf("%s",ss);
scanf("%d",&n);
while(n--){
scanf("%s",s);
ac.insert(s);
}
ac.build();
ac.count(ss);
ss[a]='\0';
printf("%s\n",ss);
return 0;
}