AC自动机(板子总结)

板子来源

https://www.luogu.com.cn/blog/zcysky/solution-p3808

心得

后续更新:

刷了洛谷之后发现,自己之前暴力跳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;
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值