为什么说是简易版?

因为复杂度大概是\(O(M*\overline N)\),而似乎还有另一种大概是\(O(M+\sum N)\)的。

不过据说比赛不会卡前一种做法,因为模式串一般不会很长。


那么步入正题。

对于\(trie\)树和\(KMP\)的预备知识就不多赘述了。

  • 下个定义

对于\(trie\)树的每个节点维护一个\(fail\)指针。

我们当然可以感性把\(fail\)指针和\(KMP\)的失配函数连接起来,并认为它在 某节点在与文本串匹配失败后 指示应该跳到哪继续匹配 的东西。

对它的稍准确定义是:节点\(i\)的\(fail\)即为 \(i\)所在链\(L_1\) 在\(trie\)树 的 以二重子节点为头的 最长的 为其(\(L_1\))后缀的链\(L_2\) 的尾节点位置。

解释一下,我们找到这样一条链\(L_2\),满足它为\(L_1\)的后缀(比如对\(L_1\)为abcd,bcd,cd,d都是),并且要求\(L_2\)的头节点为\(trie\)种深度为2的节点。此时,\(fail\)指向\(L_2\)的尾巴节点。

特别的:

  1. 当不存在这样的链时,\(fail\)指向根节点
  2. 对于节点\(i\)不存在的儿子\(j\),我们将它的位置信息等价于节点\(i\)的\(fail\)指针。(这与匹配有关)
  • 下面说说两个操作

    1.构建\(fail\)指针

    我们尽可能的用之前的状态求解当前的状态,而\(bfs\)是一种层次遍历,我们使用\(bfs\)

    当求解节点\(i\)的儿子\(j\)的\(fail\)指针:

    若儿子\(j\)存在,把\(i\)的\(fail\)指针所指向位置 的儿子\(j'\)(即\(j\)和\(j'\)为同一个字符)的位置信息 作为\(j\)的\(fail\)指针指向(当然,可能\(i\)的\(fail\)指针根本不存在这样一个儿子,但是并没有影响)

    否则,直接把儿子的位置信息指向\(i\)的\(fail\)的儿子\(j'\)的位置信息。

参考代码:

void ac_build_fail()
{
for(int i=0;i<26;i++)
if(t[0].son[i])
q.push(t[0].son[i]);
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=0;i<26;i++)
if(t[now].son[i])
{
t[t[now].son[i]].fail=t[t[now].fail].son[i];
q.push(t[now].son[i]);
}
else
t[now].son[i]=t[t[now].fail].son[i];
}
}

2.进行匹配。

当匹配到文本串的位置\(i\)时,假使我们已经在前边找到了它在\(trie\)树的最深合法匹配,我们遍历由这个点引发的\(fail\)指针组成的一条链,直到找到根节点,当遇到单词末尾时,执行相应的操作。

然后我们向下寻找\(i+1\)是否存在,如果不存在,用被\(fail\)指针等效了的节点位置信息跳转。

参考代码:

void match()
{
int len=strlen(message),now=0;
for(int i=0;i<len;i++)
{
now=t[now].son[message[i]-'a'];
for(int j=now;j;j=t[j].fail)
//some operations
}
}

模板题:洛谷P3796 【模板】AC自动机(加强版)

题目描述

有\(N\)个由小写字母组成的模式串以及一个文本串\(T\)。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串\(T\)中出现的次数最多。

输入输出格式

输入格式:

输入含多组数据。

每组数据的第一行为一个正整数\(N\),表示共有\(N\)个模式串,\(1≤N≤150\) 。

接下去\(N\)行,每行一个长度小于等于\(70\)的模式串。下一行是一个长度小于等于\(10^6\)的文本串\(T\) 。

输入结束标志为\(N=0\) 。

输出格式:

对于每组数据,第一行输出模式串最多出现的次数,接下去若干行每行输出一个出现次数最多的模式串,按输入顺序排列。

参考代码:

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int N=1000010;
const int M=151;
char message[N],word[M][N];
int ans[M];
int n,cnt=0;
struct node
{
int fail,cnt;
int son[26];
}t[12000]; void add(int k)
{
scanf("%s",word[k]);
int now=0,len=strlen(word[k]);
for(int i=0;i<len;i++)
{
int word0=word[k][i]-'a';
if(!t[now].son[word0])
t[now].son[word0]=++cnt;
now=t[now].son[word0];
}
t[now].cnt=k;
} queue <int > q;
void ac_build_fail()
{
for(int i=0;i<26;i++)
if(t[0].son[i])
q.push(t[0].son[i]);
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=0;i<26;i++)
if(t[now].son[i])
{
t[t[now].son[i]].fail=t[t[now].fail].son[i];
q.push(t[now].son[i]);
}
else
t[now].son[i]=t[t[now].fail].son[i];
}
} void match()
{
int len=strlen(message),now=0;
for(int i=0;i<len;i++)
{
now=t[now].son[message[i]-'a'];
for(int j=now;j;j=t[j].fail)
ans[t[j].cnt]++;
}
} int main()
{
scanf("%d",&n);
while(n)
{
cnt=0;
memset(t,0,sizeof(t));
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;i++) add(i);
ac_build_fail();
t[0].fail=0;
scanf("%s",message);
match();
int m_max=0;
for(int i=1;i<=n;i++)
m_max=max(m_max,ans[i]);
printf("%d\n",m_max);
for(int i=1;i<=n;i++)
if(ans[i]==m_max)
printf("%s\n",word[i]);
scanf("%d",&n);
}
return 0;
}

2018.5.22

简易版AC自动机的更多相关文章

  1. 简单版AC自动机

    简单版\(AC\)自动机 学之前听别人说起一直以为很难,今天学了简单版的\(AC\)自动机,感觉海星,只要理解了\(KMP\)一切都好说. 前置知识:\(KMP\)(有链接) 前置知识:\(Trie\ ...

  2. java版AC自动机

    class Trie { int [][]Next=new int[500005][128]; int []fail=new int[500005]; int []end=new int[500005 ...

  3. 多模字符串匹配算法之AC自动机—原理与实现

    简介: 本文是博主自身对AC自动机的原理的一些理解和看法,主要以举例的方式讲解,同时又配以相应的图片.代码实现部分也予以明确的注释,希望给大家不一样的感受.AC自动机主要用于多模式字符串的匹配,本质上 ...

  4. hdu 1277 AC自动机入门(指针版和数组版)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1277 推荐一篇博客(看思路就可以,实现用的是java): https://www.cnblogs.co ...

  5. 模板】AC自动机(简单版)

    模板]AC自动机(简单版) https://www.luogu.org/problemnew/show/P3808 这是一道简单的AC自动机模板题. 用于检测正确性以及算法常数. 为了防止卡OJ,在保 ...

  6. luogu3808 luogu3796 AC自动机(简单版) AC自动机(加强版)

    纪念一下我一晚上写了八遍AC自动机 这是加强版的: #include <iostream> #include <cstring> #include <cstdio> ...

  7. 【模版】AC自动机(简单版)

    题目背景 这是一道简单的AC自动机模版题. 用于检测正确性以及算法常数. 为了防止卡OJ,在保证正确的基础上只有两组数据,请不要恶意提交. 题目描述 给定n个模式串和1个文本串,求有多少个模式串在文本 ...

  8. 洛谷P3808 【模板】AC自动机(简单版)

    题目背景 这是一道简单的AC自动机模板题. 用于检测正确性以及算法常数. 为了防止卡OJ,在保证正确的基础上只有两组数据,请不要恶意提交. 管理员提示:本题数据内有重复的单词,且重复单词应该计算多次, ...

  9. 【模板】AC自动机(简单版)

    我:“woc...AC自动机?” 我:“可以自动AC???” 然鹅... 大佬:“傻...” 我:“(⊙_⊙)?” 大佬:“缺...” 我:“......” (大佬...卒 | 逃...) emm.. ...

随机推荐

  1. [转]Office导入导出组件权限配置汇总

    原文地址:Office导入导出组件权限配置汇总 具体配置方法如下:  1:在服务器上安装office的Excel软件.  2:在"开始"->"运行"中输入 ...

  2. (7)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 利用Polly+AOP+依赖注入封装的降级框架

    创建简单的熔断降级框架 要达到的目标是: 参与降级的方法参数要一样,当HelloAsync执行出错的时候执行HelloFallBackAsync方法. public class Person { [H ...

  3. apache工作模式总结及网站访问缓慢处理记录

    apache目前主要有两种模式:prefork模式和worker模式:1)prefork模式(默认模式)prefork是Unix平台上的默认(缺省)MPM,使用多个子进程,每个子进程只有一个线程.每个 ...

  4. webpack详细配置解析

    阅读本文之前,先看下面这个webpack的配置文件,如果每一项你都懂,那本文能带给你的收获也许就比较有限,你可以快速浏览或直接跳过:如果你和十天前的我一样,对很多选项存在着疑惑,那花一段时间慢慢阅读本 ...

  5. 阿里云OSS下载pdf文件,并在pdf文件上添加水印

    代码: 兵马未动,粮草先行 作者: 传说中的汽水枪 如有错误,请留言指正,欢迎一起探讨. 转载请注明出处. 公司要求从阿里云OSS下载pdf文件并且需要添加水印. 因此这里总结一下. 首先添加了一个F ...

  6. 多校寒训TaoTao要吃鸡dp

    题目描述 Taotao的电脑带不动绝地求生,所以taotao只能去玩pc版的荒野行动了, 和绝地求生一样,游戏人物本身可以携带一定重量m的物品,装备背包 之后可以多携带h(h为0代表没有装备背包)重量 ...

  7. B. Vasya and Isolated Vertices

    链接 [http://codeforces.com/contest/1065/problem/B] 题意 给你n个点,m条边,让你找最多孤立点和最少孤立点,不能有自环路 分析 对于最少max(0,n- ...

  8. C#【结对编程作业】小学数学习题助手

    一.软件成品展示 软件本体下载(包括程序及其更新日志,源码工程包,UML图,API接口文档,算法介绍文档,算式计算excel实例,浅查重程序) 链接: http://pan.baidu.com/s/1 ...

  9. [北航矩阵理论A]课程笔记

    [北航矩阵理论A]课程笔记 一.特征值 特征根相关: 设任一方阵 \(A = (a_{ij})_{n\times n} \in C^{n\times n}\) 特征多项式 \(T(\lambda)=| ...

  10. linux第四次读书笔记

    第四章:进程调度 一.多任务 1.非抢占式多任务 进程会一直执行直到自己主动停止运行(这一步骤称为让步) 2.抢占式多任务 Linux/Unix使用的是抢占式的方式:强制的挂起进程的动作就叫做抢占.进 ...