前言

一直听说\(AC\)自动机是一个很难很难的算法,而且它不在\(NOIP\)提高组范围内(这才是关键),所以我一直没去学。

最近被一些字符串题坑得太惨,于是下定决心去学\(AC\)自动机。

简介

\(AC\)自动机是一个著名的多模字符串匹配算法,建立在\(KMP\)算法\(Trie\)字典树的基础之上。

其实,它的本质就相当于在一棵\(Trie\)上跑\(KMP\),真是一个十分强势的算法。

\(Trie\)的作用

不得不说,在\(AC\)自动机的实现中,\(Trie\)起到了很大的作用:因为我们用它存下了每一个用来与文本串匹配的模式串。

我们可以新建一棵\(Trie\)如下:

struct Trie
{
    int Son[26],sum,Next;//Son记录当前节点的儿子的位置,sum记录当前节点包含的字符串个数,Next记录失配指针
}node[N+5];

然后将每一个模式串插入\(Trie\)中,就完成一开始的存储部分了:

inline void Insert(string s)//Trie的插入真的十分简洁
{
    register int i;int x=rt;//x记录当前节点
    for(i=0;i<s.length();++i)
    {
        int p=s[i]-'a';//p记录下一个节点的编号
        if(!node[x].Son[p]) node[x].Son[p]=++tot;//如果下一个节点不存在,就新建一个节点
        x=node[x].Son[p];//将x更新为下一个节点
    }
    ++node[x].sum;//将最终到达的节点所包含字符串的个数加1
}

神奇的失配指针

\(AC\)自动机(\(KMP\)算法)的精髓就在于失配指针\(Next\)(许多人把\(AC\)自动机的失配指针称为\(fail\),不过,由于我习惯把预处理出\(KMP\)中的\(Next\)数组的函数称为\(GetNext()\),换成\(GetFail()\)恐怕不太吉利......因此我依然用\(Next\)来表示失配指针)。

与\(KMP\)中的失配指针有点区别,\(AC\)自动机中的失配指针指向的是当前匹配到的字符串的最长后缀

如何求失配指针

我们可以写一个函数\(GetNext()\)来求出失配指针。

记得我在有关\(KMP\)的一篇博客中提到过,求\(Next\)数组的过程就是一个\(KMP\)的过程,不得不说,\(AC\)自动机也是类似的。

不过,求失配指针的过程有点像一个\(BFS\),我们可以用一个队列来存储访问到的字符串,然后每次都求出队首的一个字符串(这样可以保证每次取出的字符串的长度是递增的),求出它的失配指针。

代码如下:

inline void GetNext()//求出失配指针,类似于广搜
{
    register int i,k;q.push(rt);//初始化队列
    while(!q.empty())//只要队列中还有元素
    {
        k=q.front(),q.pop();//取出队首的元素
        for(i=0;i<26;++i)//枚举这个元素的每一个子节点
        {
            if(k^rt)//如果当前的元素不是根节点
            {
                if(!node[k].Son[i]) node[k].Son[i]=node[node[k].Next].Son[i];//如果当前节点这个儿子不存在,就将当前节点的失配指针的儿子作为当前节点的儿子
                else node[node[k].Son[i]].Next=node[node[k].Next].Son[i],q.push(node[k].Son[i]);//如果当前节点有这个儿子,就将当前节点的儿子的失配指针指向当前节点的失配指针的这个儿子,并将当前节点加入队列
            }
            else//如果当前元素是根节点就特殊处理
            {
                if(!node[k].Son[i]) node[k].Son[i]=rt;
                else node[node[k].Son[i]].Next=rt,q.push(node[k].Son[i]);
            }
        }
    }
}

\(AC\)自动机的简单实现

好了,讲完了失配指针,\(AC\)自动机的核心代码应该就很简单了吧。

这里以洛谷上一道简单的板子题为例,来贴一份代码:

inline void AC_Automation()//AC自动机的核心代码
{
    register int i,j,x=rt,len=st.length();//x记录当前到达节点
    for(GetNext(),i=0;i<len;++i)//枚举文本串上的每一个字符
    {
        if(!(x=node[x].Son[st[i]-97])) {x=rt;continue;}
        int p=x;//用p来记录当前能匹配到的字符
        while(p^rt)//只要p没有指向根
        {
            if(node[p].Cnt>=0) ans+=node[p].Cnt,node[p].Cnt=-1;//如果当前节点未被访问过,就更新匹配成功的字符串个数,并标记当前节点为已访问
            else break;//否则退出循环,因为如果当前节点访问过了,那么当前节点失配指针指向的位置肯定也访问过了
            p=node[p].Next;//更新当前节点为当前节点的失配指针
        }
    }
}

\(AC\)自动机的小应用

毕竟,\(AC\)自动机的题目不可能直接出裸题让你做字符串匹配的。

通常都只是一些小应用:

【洛谷3796】【模板】AC自动机(加强版)

【BZOJ4327】[JSOI2012] 玄武密码

【BZOJ3940】[USACO2015 Feb] Censoring

【BZOJ3172】[TJOI2013] 单词

初学AC自动机的更多相关文章

  1. bzoj 3172 AC自动机

    初学AC自动机,要先对于每一个模式串求出来trie树,在此基础上构建fail指针,然后在trie树加上失配边构建出整张trie图. AC自动机的原理和KMP差不多,一个节点的fail指针就是指向tri ...

  2. Hdu 5384 Danganronpa (AC自动机模板)

    题目链接: Hdu 5384 Danganronpa 题目描述: 给出n个目标串Ai,m个模式串Bj,问每个目标串中m个模式串出现的次数总和为多少? 解题思路: 与Hdu 2222  Keywords ...

  3. Codeforces 547E - Mike and Friends(AC 自动机+树状数组)

    题面传送门 好久每做过 AC 自动机的题了--做几个题回忆一下罢 AC 自动机能够解决多串匹配问题,注意是匹配,碰到前后缀的问题那多半不在 AC 自动机能解决的范围内. 在初学 AC 自动机的时候相信 ...

  4. 基于trie树做一个ac自动机

    基于trie树做一个ac自动机 #!/usr/bin/python # -*- coding: utf-8 -*- class Node: def __init__(self): self.value ...

  5. AC自动机-算法详解

    What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...

  6. python爬虫学习(11) —— 也写个AC自动机

    0. 写在前面 本文记录了一个AC自动机的诞生! 之前看过有人用C++写过AC自动机,也有用C#写的,还有一个用nodejs写的.. C# 逆袭--自制日刷千题的AC自动机攻克HDU OJ HDU 自 ...

  7. BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Sta ...

  8. BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]

    3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 3198  Solved: 1532[Submit][Status ...

  9. BZOJ 1212: [HNOI2004]L语言 [AC自动机 DP]

    1212: [HNOI2004]L语言 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1367  Solved: 598[Submit][Status ...

随机推荐

  1. 2017乌鲁木齐区域赛I(带权并查集)

    #include<bits/stdc++.h>using namespace std;int f[200010];//代表元long long rl[200010];//记rl[i]为结点 ...

  2. BZOJ 1858【线段树】

    题意:  0 a b 把 [a, b] 区间内的所有数全变成0  1 a b 把 [a, b] 区间内的所有数全变成1  2 a b 把 [a,b] 区间内的所有数全部取反  3 a b 询问 [a, ...

  3. Ocelot(四)- 认证与授权

    Ocelot(四)- 认证与授权 作者:markjiang7m2 原文地址:https://www.cnblogs.com/markjiang7m2/p/10932805.html 源码地址:http ...

  4. cf780E(dfs)

    题目链接: http://codeforces.com/problemset/problem/780/E 题意: 给出一个 n 个点 m 条边的图, 有 k 个人, 初始位置可以为任意位置, 每个人最 ...

  5. NAT模式下设置 虚拟机linux(Centos7) 联网

    第一步 设置虚拟机网络为NAT模式 第二步 设置虚拟机网络配置 首先执行 cd /etc/sysconfig/network-scripts 之后VI 编辑 ifcfg-ens33(根据实际情况来 基 ...

  6. dorado开发模式下实现动态查询

    使用dorado开发模式,我们可以实现以下开发技巧 开发技巧1.实现动态查询功能: 1. 查询按钮的onClick事件中写入: datasetEmployee.parameters().setValu ...

  7. AT2161 シャッフル / Shuffling

    传送门 其实有一个显然的性质嘛:对于每个数,其实只要考虑它最右能被换到的位置就好了 然后设\(f[i][j]\)表示已经处理完了前\(i-1\)位,当前还有\(j\)个\(1\)可以自由支配(注意这里 ...

  8. jenkins+maven+Tomcat+shell构建自动化部署

    https://yq.aliyun.com/articles/685931 1.官网下载war包:jenkins本质上就是一个web应用,直接下载jenkins的war包通过tomcat运行即可.ht ...

  9. FileTest

    package com.yd.wmsc.util; import java.io.File; public class FileTest { public static void main(Strin ...

  10. linux tcpdump抓取HTTP包的详细解释

    tcpdump tcpdump是linux系统自带的抓包工具,主要通过命令行的方式,比较适合在线上服务器进行抓包操作,如果是windows或者ubuntu完全可 以选择一些图形化的工具,ubuntu比 ...