首页 > 其他分享 >AC自动机

AC自动机

时间:2022-11-14 17:58:12浏览次数:39  
标签:AC ch int trie fail 自动机 root define

AC自动机以trie为基础,首先将若干模式串插入trie树中,之后构建fail指针和AC自动机,即由trie树变为trie图。

fail指针的定义是,对于当前点x,从根到x形成的字符串为s,x的fail指针指向y,其中满足根到y形成的字符串t是s的最长后缀。

BFS构建AC自动机,第二层的所有节点的fail指针都要指向trie树的根节点。

通过修改trie树的结构,增加了转移关系,避免构建时一直while跳fail。

对于通配符,若一种通配符可以匹配无数多个字符,那么将该点向自己连边形成自环即可。

struct ACAutomaton{
    struct tree{
        int ch[26],fail,cnt;
    }t[N];
    int tot,root;
    #define ch(p,x) (t[p].ch[x])
    #define f(p) (t[p].fail)
    #define c(p) (t[p].cnt)
    inline void insert(string s){
        int p=root,n=s.size();
        for(int i=0;i<n;i++){
            int c=s[i]-'a';
            if(!ch(p,c))ch(p,c)=++tot;
            p=ch(p,c);
        }
        c(p)++;/*当前字符串出现次数*/
    }
    inline void makefail(){
        queue<int>q;
        for(int i=0;i<26;i++)if(ch(root,i))q.emplace(ch(root,i));/*第二层所有节点入队*/
        while(!q.empty()){
            int x=q.front();
            q.pop();
            for(int i=0;i<26;i++){
                if(ch(x,i)){/*如果存在该子节点*/
                    f(ch(x,i))=ch(f(x),i);/*子节点的fail是当前点fail对应的相同子节点*/
                    q.emplace(ch(x,i));/*之后入队*/
                }
                else ch(x,i)=ch(f(x),i);/*子节点指向当前点fail的对应子节点*/
            }
        }
    }
    inline int count(string s){/*询问trie树中的字符串在s中出现的个数*/
        int p=root,n=s.size(),re=0;
        for(int i=0;i<n;i++){
            int c=s[i]-'a';
            p=ch(p,c);/*在trie树上跳*/
            for(int j=p;j&&c(j)!=-1;j=f(j)){/*一个点可以匹配成功,则它的fail也可以匹配成功*/
                re+=c(j);
                c(j)=-1;/*标记已经加过了*/
            }
        }
        return re;
    }
};

给定一些模式串和一个文本串,求在文本串中出现次数最多的模式串出现的次数和这些串的内容。在构建trie树时维护是哪一个串,插入时在字符串末尾节点标记一下,查询的时候暴力跳fail并对这个节点表示的id的答案累加,不标记访问,找出每个id对应ans的最大值即可,注意不要直接对ans进行更新,只有当节点是某个字符串末尾时才进行更新。

    struct tree{
        int ch[26],fail,idx;
        inline void clear(){
            memset(this,0,sizeof(*this));
        }
    }t[N];
    int tot,root;
    #define ch(p,x) (t[p].ch[x])
    #define f(p) (t[p].fail)
    #define i(p) (t[p].idx)
    inline void insert(string s,int idx){
        int p=root,n=s.size();
        for(int i=0;i<n;i++){
            int c=s[i]-'a';
            if(!ch(p,c))ch(p,c)=++tot;
            p=ch(p,c);
        }
        i(p)=idx;
    }
    inline void query(string s){
        int p=root,n=s.size();
        for(int i=0;i<n;i++){
            int c=s[i]-'a';
            p=ch(p,c);
            for(int j=p;j;j=f(j)){
                if(i(j)==0)continue;
                res[i(j)]++;
                ans=max(ans,res[i(j)]);
            }
        }
    }

求每个模式串在文本串中出现的次数。在建出fail树后,记录自动机上每个状态被匹配了多少次,求出每个文本串在fail树上的终止点的子树和即可。由于fail树是由一个点的fail指向的点向该点连边,在bfs时父节点都是先被遍历到,所以可以不用构建fail树,直接从最后入队的节点开始向前扫,自己fial的次数累加上自己的次数,这样的话就最好手写队列。

    struct tree{
        int ch[26],fail,sum;
    }t[N];
    int tot,root,pos[N],q[N],l=1,r=0;
    #define ch(p,x) (t[p].ch[x])
    #define f(p) (t[p].fail)
    #define s(p) (t[p].sum)
    inline void insert(string s,int id){
        int p=root,n=s.size();
        for(int i=0;i<n;i++){
            int c=s[i]-'a';
            if(!ch(p,c))ch(p,c)=++tot;
            p=ch(p,c);
        }
        pos[id]=p;
    }
    inline void makefail(){
        l=1,r=0;
        for(int i=0;i<26;i++)if(ch(root,i))q[++r]=(ch(root,i));
        while(l<=r){
            int x=q[l++];
            for(int i=0;i<26;i++){
                if(ch(x,i)){
                    f(ch(x,i))=ch(f(x),i);
                    q[++r]=(ch(x,i));
                }
                else ch(x,i)=ch(f(x),i);
            }
        }
    }
    inline void query(string s){
        int p=root,n=s.size();
        for(int i=0;i<n;i++){
            int c=s[i]-'a';
            p=ch(p,c);
            s(p)++;
        }
        for(int i=r;i;i--)s(f(q[i]))+=s(q[i]);
    }
    inline int find(int x){
        return s(pos[x]);
    }

给定一个列表和一个文本串s,每次删除列表中最早出现在s中的单词,求最后的s.维护两个栈,在AC自动机上匹配s时,若匹配成功,则将栈顶跳到匹配该单词之前,也就是减去这个单词的长度,一个栈维护trie树上的节点,一个栈维护对应的下标。

    struct tree{
        int ch[26],fail,len;
    }t[N];
    int tot,root,top;
    pair<int,int>stk[N];
    #define ch(p,x) (t[p].ch[x])
    #define f(p) (t[p].fail)
    #define l(p) (t[p].len)
    inline void insert(string s){
        int p=root,n=s.size();
        for(int i=0;i<n;i++){
            int c=s[i]-'a';
            if(!ch(p,c))ch(p,c)=++tot;
            p=ch(p,c);
        }
        l(p)=n;
    }
    inline void solve(string s){
        int p=root,n=s.size();
        for(int i=0;i<n;i++){
            int c=s[i]-'a';
            p=ch(p,c);
            stk[++top]={p,i};
            p=stk[top-=l(p)].first;/*如果不是一个字符串的末尾时l(p)=0,相当于没有变动*/
        }
        for(int i=1;i<=top;i++)cout<<s[stk[i].second];
    }

支持三种操作,添加编号为i的字符串,删除编号为i的字符串,查询已有字符串集合中的字符串在询问的文本串中出现的次数,添加时若已有应忽略,删除时若已经没有应忽略。构建fail树后,找到当前字符串的结尾节点,删除操作就是在该节点的子树内全部减一,添加就是全部加一,查询时就是对文本串在trie树上跑,每个点累加子树和,可以用树状数组去检修盖单点查询维护。

    struct tree{
        int ch[26],fail,fa;
    }t[N];
    int tot,root,pos[N],ipt[N],opt[N],dfn;
    #define ch(p,x) (t[p].ch[x])
    #define f(p) (t[p].fail)
    inline void insert(string s,int idx){
        int p=root,n=s.size();
        for(int i=0;i<n;i++){
            int c=s[i]-'a';
            if(!ch(p,c))ch(p,c)=++tot;
            p=ch(p,c);
        }
        pos[idx]=p;
    }
    inline void makefail(){
        queue<int>q;
        for(int i=0;i<26;i++)if(ch(root,i))q.emplace(ch(root,i)),v[root].emplace_back(ch(root,i));
        while(!q.empty()){
            int x=q.front();
            q.pop();
            for(int i=0;i<26;i++){
                if(ch(x,i)){
                    f(ch(x,i))=ch(f(x),i);
                    q.emplace(ch(x,i));
                    v[f(ch(x,i))].emplace_back(ch(x,i));
                }
                else ch(x,i)=ch(f(x),i);
            }
        }
    }
    void dfs(int x){
        ipt[x]=++dfn;
        for(auto y:v[x])dfs(y);
        opt[x]=dfn;
    }
    inline int query(string s){
        int p=root,n=s.size(),re=0;
        for(int i=0;i<n;i++){
            int c=s[i]-'a';
            p=ch(p,c);
            re+=bit.ask(ipt[p]);
        }
        return re;
    }
    aca.dfs(0);
    bit.n=aca.dfn;
    for(int i=1;i<=n;i++)bit.add(aca.ipt[aca.pos[i]],aca.opt[aca.pos[i]],1);
    while(q--){
        string s;
        cin>>s;
        if(s[0]=='?'){
            cout<<aca.query(s)<<'\n';
            continue;
        }
        int j=0,x=0;
        while(s[++j])x=(x<<1)+(x<<3)+(s[j]^48);
        if(s[0]=='-'&&!del[x]){
            del[x]=1;
            bit.add(aca.ipt[aca.pos[x]],aca.opt[aca.pos[x]],-1);
        }
        else if(s[0]=='+'&&del[x]){
            del[x]=0;
            bit.add(aca.ipt[aca.pos[x]],aca.opt[aca.pos[x]],1);
        }
    }

标签:AC,ch,int,trie,fail,自动机,root,define
From: https://www.cnblogs.com/safeng/p/16889797.html

相关文章

  • Manacher
    通过在字符串之间的每个空位内插入字符,是的奇数和偶数成为一样得情况,从而能够统一处理。用len[]记录每个点能够扩展出的最长半径,mx记录扩展到的最远右端点,id记录mx为右端......
  • [Mac]在Mac上关闭Microsoft AutoUpdate弹框提示
    打开terminal终端cd/Library/Application\Support/Microsoft/MAU2.0输入文件地址sudochmod000Microsoft\AutoUpdate.app将此应用程序权限设置为000输入密码......
  • Apple Safari 16.1 - macOS 专属浏览器 (独立安装包下载)
    Safari浏览器16.1forMontery,BigSur请访问原文链接:AppleSafari16.1-macOS专属浏览器(独立安装包下载),查看最新版。原创作品,转载请保留出处。作者主页:www.sys......
  • 阿里云 ACK 接入观测云
    简介容器服务Kubernetes版(简称ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。2021年成为国内唯一连续三年入选Gartner公共云容器报......
  • Oracle group by over(partition by order by)相关
    快速理解:groupby使用一个(多个)含重复数据的字段进行表数据合算(聚合),结果集展示聚合结果。partitionby同样适用于含重复数据的一个(多个)字段,但是不进行聚合,只是在结果集......
  • Oracle 数据库 19c Home 克隆
             众所周知,oracle数据库软件堆栈以部署起来复杂而著称,早期oracle8i/9i/10g/11g时代,数据库软件环境部署占据dba相当大的工作量,稍不慎很容易部署失败,造......
  • Rt?ABC中,AC⊥BC,∠B=15°,CB=4.求AC长度?
    END......
  • 「React」PropTypes提供的验证器
    前言通常,我们在项目中使用自定义组件时,需要对组件的props进行类型检测。而React提供了专门的库,可以校验组件的props类型,也可以做一些特定的限制。下面行详细介绍。以下内容......
  • ActiveMQ经典的使用模式(利用多线程处理消费端)
    今天看视频,里面讲了一个经典的例子,是工作中很常用的,特此将这种模式记录下来.这个例子使用了ActiveMQ的选择器,也使用了之前学的自定义线程池.队列的使用,而且很好的利......
  • GORMACS如何使用?一个方法快速完成动力学模拟计算
    GROMACS是一个功能强大的分子动力学的模拟软件,其在模拟大量分子系统的牛顿运动方面具有极大的优势。它可以用分子动力学、随机动力学或者路径积分方法模拟溶液或晶体中的任......