板子来源
题解 P3808 【【模板】AC自动机(简单版)】 - 洛谷专栏
心得
刷了洛谷之后发现,自己之前暴力跳fail可以被卡,改成trie图优化的板子了
板子整理
P3808【模板】AC自动机(简单版)
统计一下文本串里,模式串一共出现过几个,其中相同的只算一次,保证无任意两个模式串相同
#include<bits/stdc++.h>
#define N 500010
using namespace std;
namespace AC{
int nex[N][26],num[N],fail[N],c;
void init(){
c=0;
memset(nex[c],0,sizeof nex[c]);
fail[c]=num[c]=0;
}
void ins(char *s,int n){
int rt=0;
for(int i=0;i<n;i++){
int v=s[i]-'a';
if(!nex[rt][v]){
nex[rt][v]=++c;
memset(nex[c],0,sizeof nex[c]);
fail[c]=num[c]=0;
}
rt=nex[rt][v];
}
num[rt]++;
}
void build(){
queue<int>q;
for(int i=0;i<26;i++){
if(nex[0][i]){
fail[nex[0][i]]=0,q.push(nex[0][i]);
}
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;i++){
if(nex[u][i])fail[nex[u][i]]=nex[fail[u]][i],q.push(nex[u][i]);
else nex[u][i]=nex[fail[u]][i];
}
}
}
int query(char *s,int n){
int rt=0,ans=0;
for(int i=0;i<n;i++){
rt=nex[rt][s[i]-'a'];
for(int j=rt;j && ~num[j];j=fail[j]){//防止重搜
ans+=num[j],num[j]=-1;
}
}
return ans;
}
};
using namespace AC;
int n;
char p[1000005];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",p);
ins(p,strlen(p));
}
build();
scanf("%s",p);
printf("%d\n",query(p,strlen(p)));
return 0;
}
P3796 【模板】AC自动机(加强版)
文本串中,输出最大的模式串出现次数,及对应的所有模式串,保证无任意两个模式串相同
多模式匹配,统计每个串的出现次数,经典题目;
暴力跳fail的复杂度据说不大正确,所以像SAM那样,
打差分标记,即沿途经过的点标记num值加1,实际它是一个应该对其所有fail都+1的差分标记,
再按fail树由叶子向根做前缀和,实际上这个顺序是插入时的逆序,用队列存下来即可
#include<bits/stdc++.h>
#define N 70*150+5
using namespace std;
namespace AC{
int nex[N][26],num[N],id[N],fail[N],q[N],h,t,c;
void init(){
c=0;
memset(nex[c],0,sizeof nex[c]);
fail[c]=num[c]=id[c]=0;
}
void ins(char *s,int n,int x){
int rt=0;
for(int i=0;i<n;i++){
int v=s[i]-'a';
if(!nex[rt][v]){
nex[rt][v]=++c;
memset(nex[c],0,sizeof nex[c]);
fail[c]=num[c]=id[c]=0;
}
rt=nex[rt][v];
}
id[rt]=x;
}
void build(){
h=1;t=0;
for(int i=0;i<26;i++){
if(nex[0][i]){
fail[nex[0][i]]=0,q[++t]=nex[0][i];
}
}
while(h<=t){
int u=q[h++];
for(int i=0;i<26;i++){
if(nex[u][i])fail[nex[u][i]]=nex[fail[u]][i],q[++t]=nex[u][i];
else nex[u][i]=nex[fail[u]][i];
}
}
}
void query(char *s,int n){
int rt=0;
for(int i=0;i<n;i++){
rt=nex[rt][s[i]-'a'];
num[rt]++;
}
}
};
using namespace AC;
int n;
char txt[155][75],p[1000005];
int main(){
while(~scanf("%d",&n) && n){
init();
for(int i=1;i<=n;i++){
scanf("%s",txt[i]);
ins(txt[i],strlen(txt[i]),i);
}
build();
scanf("%s",p);
query(p,strlen(p));
for(int i=t;i>=1;--i){
int v=q[i];
num[fail[v]]+=num[v];
}
int mx=0;
for(int i=1;i<=c;++i){
if(id[i])mx=max(mx,num[i]);
}
printf("%d\n",mx);
for(int i=1;i<=c;++i){
if(id[i] && mx==num[i]){
printf("%s\n",txt[id[i]]);
}
}
}
return 0;
}
P5357【模板】AC自动机(二次加强版)
统计每个模式串在文本串中出现的次数,可能存在两个或多个模式串完全相同
完全相同的,在trie树上开散列存下标即可,这里用的vector,剩下的与上一题相同
#include<bits/stdc++.h>
#define N 200005
using namespace std;
namespace AC{
int nex[N][26],num[N],fail[N],q[N],h,t,c;
vector<int>id[N];
void init(){
c=0;
memset(nex[c],0,sizeof nex[c]);
fail[c]=num[c]=0;
id[c].clear();
}
void ins(char *s,int n,int x){
int rt=0;
for(int i=0;i<n;i++){
int v=s[i]-'a';
if(!nex[rt][v]){
nex[rt][v]=++c;
memset(nex[c],0,sizeof nex[c]);
fail[c]=num[c]=0;
id[c].clear();
}
rt=nex[rt][v];
}
id[rt].push_back(x);
}
void build(){
h=1;t=0;
for(int i=0;i<26;i++){
if(nex[0][i]){
fail[nex[0][i]]=0,q[++t]=nex[0][i];
}
}
while(h<=t){
int u=q[h++];
for(int i=0;i<26;i++){
if(nex[u][i])fail[nex[u][i]]=nex[fail[u]][i],q[++t]=nex[u][i];
else nex[u][i]=nex[fail[u]][i];
}
}
}
void query(char *s,int n){
int rt=0;
for(int i=0;i<n;i++){
rt=nex[rt][s[i]-'a'];
num[rt]++;
}
}
};
using namespace AC;
int n,ans[N];
char p[2000005];
int main(){
scanf("%d",&n);
init();
for(int i=1;i<=n;i++){
scanf("%s",p);
ins(p,strlen(p),i);
}
build();
scanf("%s",p);
query(p,strlen(p));
for(int i=t;i>=1;--i){
int v=q[i];
num[fail[v]]+=num[v];
}
for(int i=1;i<=c;++i){
for(int j=0;j<id[i].size();++j){
int x=id[i][j];
ans[x]=num[i];
}
}
for(int i=1;i<=n;++i){
printf("%d\n",ans[i]);
}
return 0;
}