• [模板] AC自动机


    AC 自动机

    AC 自动机 (Aho-Corasick Automation) 是一个多模式字符串匹配算法.

    定义 (fail) 函数为该状态 (节点) 代表的字符串的最长后缀所在的状态. 特别的, 如果不存在这样的状态, (fail) 函数设为根.

    为了提高构建和匹配的效率, 在建立 trie 树之后, 定义

    [child(p, c) = egin{cases} v & (p 存在 c 的转移) \ child(fail(p), c) & (不断跳 fail 函数到第一个有 c 的转移的状态) \ root & (不存在这样的状态) end{cases} ]

    也可以把这种定义了新的 (child) 转移的 AC 自动机称为 trie 图.

    这样的话, 设 (N) 为 trie 树的节点数, (Sigma) 为字符集大小, 模式串的长度和为 (T), 文本串的长度为 (S),
    构建的时间复杂度为 (O(N cdot Sigma)), 空间复杂度为 (O(N cdot Sigma));
    匹配的时间复杂度为 (O(S)).

    另一种实现是:

    不补全不存在的转移, 并且利用 unsigned int 压位维护 (Sigma) 个转移是否存在, 求 (fail) 函数 / 匹配的过程直接跳 (fail) 函数.

    可以利用势能分析证明, 这样的时间复杂度为 (O(T)).

    代码

    const int ndsz=1e6+50;
    struct tac{
        int ch[ndsz][30],fi[ndsz],end[ndsz],pn=0,rt=0;
        int tr(int v){return v-'a'+1;}
        int newnd(){return ++pn;}
        void clear(){
            memset(ch,0,sizeof(ch));
            memset(fi,0,sizeof(fi));
            memset(end,0,sizeof(end));
            pn=0,rt=0;
        }
        void insert(char *s){
            int p=rt,v;
            for(int i=0;s[i];++i){
                v=tr(s[i]);
                if(ch[p][v]==0)ch[p][v]=newnd();
                p=ch[p][v];
            }
            ++end[p];
        }
        void build(){
            static int qu[ndsz],qh=1,qt=0;
            rep(i,1,26)if(ch[rt][i])fi[ch[rt][i]]=0,qu[++qt]=ch[rt][i];
            while(qh<=qt){
                int u=qu[qh++];
                rep(i,1,26){
                    if(ch[u][i]==0)ch[u][i]=ch[fi[u]][i];
                    else fi[ch[u][i]]=ch[fi[u]][i],qu[++qt]=ch[u][i];
                }
            }
        }
        int match(char *s){
            int now=rt,ans=0;
            for(int i=0;s[i];++i){
                now=ch[now][tr(s[i])];
                for(int p=now;p&&~end[p];p=fi[p])ans+=end[p],end[p]=-1; //每个模式串只匹配一次
            }
            return ans;
        }
        void pr(){
            printf("pn=%d,rt=%d
    ",pn,rt);
            rep(i,0,pn){
                printf("fi=%d,end=%d,",fi[i],end[i]);
                rep(j,1,26)printf("%d ",ch[i][j]);
                printf("
    ");
            }
        }
    }ac;
    

    附加的应用

    DP

    由于 AC 自动机属于 DAG , 可以在自动机上进行动态规划.

    last指针

    有时题目所求仅与关键点 (模式串终点) 有关. 可以定义 (last(p)) 表示 (p) 跳 fail 指针到达的第一个关键点.

    (last(p)) 容易通过动态规划求出.

    fail 树

    将所有fail指针反向, 可以得到一棵以原 trie 树的根为根的树, 可以称之为 fail 树.

    fail树有如下的性质:

    1. 每个节点代表某个模式串的一个前缀;
    2. 节点的父亲代表它的满足 1. 的最长后缀.

    我们还可以得到这样的一些结论:

    1. 一个节点的子树大小即为它在所有模式串中出现的次数.
    2. ...
  • 相关阅读:
    表单提交方式
    js--dom对象
    js--bom对象
    css布局的漂浮、position定位
    python语法学习第五天--lambda表达式、filter()、map()
    python语法学习第五天--函数(2)
    python3语法学习第五天--函数(1)
    python3语法学习第四天--序列
    leetcode| 84. 柱状图中最大的矩形
    leetcode| 56. 合并区间
  • 原文地址:https://www.cnblogs.com/ubospica/p/9872934.html
Copyright © 2020-2023  润新知