• AC自动机


    A C AC AC 自动机( A h o − C o r a s i c k   a u t o m a t o n Aho-Corasick \ automaton AhoCorasick automaton)在1975年产生于贝尔实验室,是著名的多模匹配算法。

    学习AC自动机之前,首先要有 K M P KMP KMP T r i e Trie Trie字典树)的基础知识。

    1、构造 T r i e Trie Trie

    我们先用 n n n 个模式串构造一颗 T r i e Trie Trie T r i e Trie Trie 中的一个节点表示一个从根到当前节点的字符串。

    如果节点是个模式串,打一个标记。

    void insert(char* s) {
        int p = 0;
        for (int i = 0; s[i]; i ++ ) {
            int j = s[i] - 'a';
            if (!tr[p][j]) tr[p][j] = ++ idx;
            p = tr[p][j];
        }
        cnt[p] ++ ;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    2、构造 A C AC AC 自动机

    T r i e Trie Trie 上构建两类边:回跳边、转移边。

    • 回跳边指向父节点的回跳边所指节点的儿子。
    • 转移边指向当前节点回跳边所指节点的儿子。转移边相当于 K M P KMP KMP 的失配指针。

    n e [ i ] ne[i] ne[i]

    • 存节点 v v v 的回跳边的终点。
    • 回跳边指向父节点的回跳边所指节点的儿子。
    • 四个点 ( v , u , n e [ u ] , c h [ ] [ ] v, u, ne[u], ch[ ][ ] v,u,ne[u],ch[][]) 构成四边形。
    • 回跳边所指节点一定是当前节点的最长后缀

    c h [ u ] [ i ] ch[u][i] ch[u][i]

    • 既存树边的终点也存转移边的终点。
    • 三个点( u , n e [ u ] , c h [ ] [ ] u,ne[u],ch[][] u,ne[u],ch[][])构成三角形。
    • 转移边所指节点一定是当前节点的最短路

    BFS构造 A C AC AC 自动机:

    • 初始化把根节点的儿子们入队。
    • 只要队不空,节点 u u u 出队,枚举 u u u 26 26 26 个儿子
      • 若儿子存在,则爹帮儿子建回跳边,并把儿子入队。
      • 若儿子不存在,则爹自建转移边。
    void build() {  //创建 AC 自动机
        queue<int> q;
        for (int i = 0; i < 26; i ++ ) {
            if (tr[0][i]) q.push(tr[0][i]);
        }
        while(q.size()) {
            int u = q.front();
            q.pop();
            for (int i = 0; i < 26; i ++ ) {
                int v = tr[u][i];
                if (v) ne[v] = tr[ne[u]][i], q.push(v);
                else tr[u][i] = tr[ne[u]][i];
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    3、扫描主串匹配

    扫描主串,依次取出字符 s [ k ] s[k] s[k]

    • i i i 指针走主串对应的节点,沿着树边或转移边走,保证不回退。
    • j j j 指针沿着回跳边搜索模式串,每次从当前节点走到根节点,把当前节点中的所有后缀模式串—网打尽,保证不漏解。
    • 扫描完主串,返回答案。
    int query(char* s) {
        int ans = 0;
        for (int i = 0, k = 0; s[k]; k ++ ) {
            i = tr[i][s[k] - 'a'];
            for (int j = i; j && ~cnt[j]; j = ne[j]) {
                ans += cnt[j];
                cnt[j] = -1;
            }
        }
        return ans;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    参考资料:

    1、《算法训练营进阶版》— 陈小玉

    2、b站董晓算法

  • 相关阅读:
    Linux 安全 - LSM机制
    创建实例化新表格及新行
    HTTP的长连接(使用selectPage分页获取总页数处理)
    算法与数据结构 --- 二叉树的性质和存储结构
    Vue笔记_03组件_mavonEditor组件(基于vue)
    基于JAVA基于Web的社区商超系统的设计与实现计算机毕业设计源码+系统+mysql数据库+lw文档+部署
    vue的一些总结
    一本通2026;阶乘和
    TeaPearce/Conditional_Diffusion_MNIST 源码阅读
    07_瑞萨GUI(LVGL)移植实战教程之LVGL对接EC11旋转编码器驱动
  • 原文地址:https://blog.csdn.net/weixin_60484917/article/details/127679895