AC自动机-洛谷3121 [USACO15FEB]审查(黄金)Censoring (Gold)
https://www.luogu.org/problem/show?pid=3121#sub
首先题目看清楚
FJ注意到列表中的单词不会出现一个单词是另一个单词子串的情况,这意味着每个列表中的单词在S中出现的开始位置是互不相同的
这意味着你在找答案时,当一个节点正常访问的时候,你不用去寻找fail;
你只要在匹配失败的时候取fail就好了;
关于fail的意义,可以看我AC自动机的博客;
然后我们在造trie时,顺便把一个字符串的长度记录下来;
end=一个字符串的长度;
然后我们开2个栈;
a[]记录当前搜到那个点;
b[]记录当前搜那个char;
然后如果发现当前节点是end;
直接弹出栈顶end个元素;
更新now指针,继续搜;
好了;
有个事情要注意;
我原始的AC自动机;
void makezyy(){
for(int i=0;i<26;i++)if(T[0].nxt[i])q[++r]=T[0].nxt[i];
while(r>l){
int x=q[++l];
for(int i=0;i<26;i++)if(T[x].nxt[i]){
q[++r]=T[x].nxt[i];
int y=T[x].fail;
while(y&&!T[y].nxt[i])y=T[y].fail;
T[q[r]].fail=T[y].nxt[i];
}
}
}
void find(int m){
int o=0;
for(int i=0;i<m;i++){
int x=s[i]-'a';
int y=o;
while(y&&!T[y].nxt[x])y=T[y].fail;
o=T[y].nxt[x];
a[++tot]=o;
b[tot]=i;
if(T[o].E){
tot-=T[o].E;
o=a[tot];
}
}
}
大家看,这个好理解,但是用了while循环,会被卡一个点;
那我们看看更优的写法;
void makezyy(){
for(int i=0;i<26;i++)if(T[0].nxt[i])q[++r]=T[0].nxt[i];
while(r>l){
int x=q[++l];
for(int i=0;i<26;i++)
if(!T[x].nxt[i])T[x].nxt[i]=T[T[x].fail].nxt[i];
else{
q[++r]=T[x].nxt[i];
T[T[x].nxt[i]].fail=T[T[x].fail].nxt[i];
}
}
}
void find(int m){
int o=0;
for(int i=0;i<m;i++){
o=T[o].nxt[s[i]-'a'];
a[++tot]=o;
b[tot]=i;
if(T[o].E){
tot-=T[o].E;
o=a[tot];
}
}
}
种方法巧妙地运用地推;
两种方法求出的fail值是相同的;
但是这种方法会无法辨别出某个节点在一开始有没有某个儿子;
因为makezyy之后空的儿子自动变成了fail;
好像还可以优化的吧;
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
struct trie{
int nxt[26],fail,E;
}T[100005];
int q[100005],l,r,a[100005],b[100005],tot;
int n,m,ll;
char s[100005],c[100005];
void init(int m){
int o=0;
for(int i=0;i<m;i++){
int x=c[i]-'a';
if(!T[o].nxt[x])T[o].nxt[x]=++ll;
o=T[o].nxt[x];
}T[o].E=m;
}
void makezyy(){
for(int i=0;i<26;i++)if(T[0].nxt[i])q[++r]=T[0].nxt[i];
while(r>l){
int x=q[++l];
for(int i=0;i<26;i++)
if(!T[x].nxt[i])T[x].nxt[i]=T[T[x].fail].nxt[i];
else{
q[++r]=T[x].nxt[i];
T[T[x].nxt[i]].fail=T[T[x].fail].nxt[i];
}
}
}
void find(int m){
int o=0;
for(int i=0;i<m;i++){
o=T[o].nxt[s[i]-'a'];
a[++tot]=o;
b[tot]=i;
if(T[o].E){
tot-=T[o].E;
o=a[tot];
}
}
}
int main()
{
scanf("%s",s);
scanf("%d",&n);
while(n--)scanf("%s",c),init(strlen(c));
makezyy();
find(strlen(s));
for(int i=1;i<=tot;i++)cout<<s[b[i]];
}