◇学时·10 & 模板·3◇ AC自动机

跟着高中上课……讲AC自动机的扩展运用。然而连KMP、trie字典树都不怎么会用的我一脸懵逼<(_ _)>

花一上午自学了一下AC自动机 QwQ


◦ Trie树

字典树的一种(听说还有其他字典树,不清楚)。每个节点代表一个字母,根节点相当于超级源点,根节点不表示字母。Trie树最大的特点是从根节点出发,沿着树边向下走,走过的节点会形成一个字符串。而一些节点是某一个单词的结尾,对于这种节点,我们一般会给它做一个标记(ovr)。

▪ 构建Trie树(Build)

根据Trie树的特点,最初的树是一个空集,只包含根节点。当我们要向树中插入一个单词str时,从根节点出发,如果根节点有表示str[0]的儿子,则移步到该儿子;否则新建立一个表示str[0]的儿子,再移步。以此类推,当我们要插入str[k]时,我们应该在第(k+1)层的某一个节点now(根节点为第一层),如果节点now有表示str[k]的儿子,则移步,否则先创建表示str[k]的儿子,再移步。直到将整个单词遍历完才结束。假设我们结束时的节点在now,那么ovr可以做两种基本的标记:① 该节点是多少个单词的结尾;② 该节点是哪一个单词的结尾……当然如果题目有一些奇怪的要求的话可以用ovr存储一些奇怪的东西,甚至多定义几个ovr也可以。

void Build(string str,int id){
int len=str.length(),now=0; //当前节点是now,trie[0]是根节点
for(int i=0;i<len;i++){
if(!trie[now].son[str[i]-'a'])
trie[now].son[str[i]-'a']=++cnt; //cnt类似于指针,用于新建节点,(cnt+1)指向最近的一个空节点
now=trie[now].son[str[i]-'a']; //移步
}
trie[now].ovr=id; //做标记,这里是存储的trie[now]是哪一个单词的结尾
}

▪ 与AC自动机的关系

AC自动机是建立在Trie树上的,只是围绕KMP的fail函数增添了一些边。


◦ KMP

一种字符串匹配算法,在朴素的字符串匹配算法的基础上进行了可观的优化。若要在字符串A里查找字符串B,则称A为“主串”,B为“模式串”,当我们尝试一次匹配时发现匹配失败,则称为“失配”。

匹配时有两个指针,i表示从主串的第i个位置开始,j表示模式串匹配到了第j个位置。当朴素算法在主串第i个位置失配时,j会回到0,而i就+1,即从主串下一个位置继续从模式串的第一个位置开始匹配,这样会造成一种浪费——下一次匹配并没有利用到之前失配的匹配的已经匹配好的信息。

而KMP算法对其进行了优化。

▪ KMP算法的原理

KMP算法认为“不需要将模式串一个位置一个位置地向右滑动”,例如:

当模式串"abca"在主串"abcd"失配后,我们没有必要将i++,因为主串的下一个位置不是'a',逐步滑动不一定会匹配。而KMP算法就会在发现失配后,直接将主串向右移动到可能匹配的最远位置!

当模式串的某一个前缀是模式串的真子串时,我们在失配后可以直接将模式串移动到该位置。

(不知道怎么解释了,看上面的3张图片吧)

▪ Fail函数

为了实现主串失配时指针不回溯,只调整模式串指针j,使模式串向右尽可能远地滑动,定义失配函数Fail(j),表示当模式串中第j个字符与主串中Si失配时,在模式串中可能和主串中Si匹配的字符的位置。

转移式则是:fail[i]=①-1(i=0);②max{ k|0<k<j, 且p0 …pk-1=pj-k+1 …pj-1 };③0(其他情况)。


◦ AC自动机

▪ 插入单词和Trie树是一样的( ̄▽ ̄)"

▪ 节点的结束单词统计也和Trie树是一样的

▪ 获取Fail函数

这里是用BFS获取的。当单词在字典树的第二层就失配即在第一个字符就失配时,fail一定是0。也就是说第二层节点的fail都指向根节点。我们将第一层的所有节点都push进队列里,然后如果节点u本来有"a"+i儿子v,则将v的fail指向u的fail的"a"+i儿子,否则直接将v指向u的fail的"a"+i儿子。

void GetFail(){
queue< int > que;
for(int i=0;i<26;i++) //遍历第二层
if(trie[0].son[i])
trie[trie[0].son[i]].fail=0,
que.push(trie[0].son[i]);
while(!que.empty()){
int u=que.front();que.pop();
for(int i=0;i<26;i++) //找儿子节点
if(trie[u].son[i]){ //有表示"a"+i的儿子
trie[trie[u].son[i]].fail=trie[trie[u].fail].son[i];
//指向父亲的fail的"a"+i儿子
que.push(trie[u].son[i]);
}
else
trie[u].son[i]=trie[trie[u].fail].son[i];
//直接将儿子指向父亲fail的"a"+i儿子
}
}

▪ 主串上的递推

设now是当前所处的节点。从根节点开始则now的初始值为0。从头到尾枚举主串字符str[i],先将now赋值为now的str[i]儿子。再沿着now的fail指针一直回溯到根节点,可以实现遍历str[0~i]的每一个后缀。对于str的每一个前缀都求出全部后缀,就相当于求出了str的全部子串。

根据题目要求统计答案。

void ACQuery(string str){
int len=str.length();
int now=0;
for(int i=0;i<len;i++){
now=trie[now].son[str[i]-'a']; //移动now
for(int j=now;j;j=trie[j].fail) //按fail指针回溯
ans[trie[j].ovr].num++; //统计答案
}
}

The End

Thanks for reading!

- Lucky_Glass

(Tab:如果我有没讲清楚的地方可以直接在邮箱lucky_glass@foxmail.com email我,在周末我会尽量解答并完善博客~)

【学时总结&模板时间】◆学时·10 & 模板·3◆ AC自动机的更多相关文章

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

    点此看题面 大致题意: 一道模板题,给你\(N\)个模式串和一个文本串,要你求出在文本串中出现次数最多的若干个模式串并输出它们. \(AC\)自动机 都说了是\(AC\)自动机的模板题,做法肯定是\( ...

  2. luoguP3796[模板]AC自动机(加强版)

    传送门 ac自动机模板,可能我写的ac自动机是有点问题的,所以跑的有些慢 暴力跳fail统计 代码: #include<cstdio> #include<iostream> # ...

  3. HDU-2222 Keywords Search(AC自动机--模板题)

    题目大意:统计一共出现了多少次模板串. 题目分析:AC自动机的模板题.不过这题有坑,相同的模板串不能只算一次. 代码如下: # include<iostream> # include< ...

  4. HDU:2222-Keywords Search(AC自动机模板,匹配模拟)

    Keywords Search Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) P ...

  5. ;~ 小部分AutoHotkey源代码片段测试模板2019年10月9日.ahk

    ;~ 小部分AutoHotkey源代码片段测试模板2019年10月9日.ahk ;~ 此脚本用于测试执行一行或多行AHK脚本源代码的效果;~ 此脚本最后修改于2019年9月22日20时03分;~ 把此 ...

  6. php实现下载模板与上传模板解析

    <? //下载模板的请求 if(isset($_GET['action']) && $_GET['action'] =='down_group_excel'){ $code = ...

  7. Django 模板 语法 变量 过滤器 模板继承 组件 自定义标签和过滤器 静态文件相关

    本节目录 一 语法 二 变量 三 过滤器 四 标签Tags 五 模板继承 六 组件 七 自定义标签和过滤器 八 静态文件相关 一 语法   模板渲染的官方文档 关于模板渲染你只需要记两种特殊符号(语法 ...

  8. (9)模板层 - templates(模板语言、语法、取值、过滤器、变量的使用)

    django的模板语言:DTL 模板语言的变量传入 这个是标签 {{ 变量名 }} {{ 变量名 }}   #模板语言的替换可以在模板中的任意位置生效 PS:通过 . 可以做深度查询 模板语言的过滤器 ...

  9. 聊聊C++模板函数与非模板函数的重载

    前言 函数重载在C++中是一个很重要的特性.之所以有了它才有了操作符重载.iostream.函数子.函数适配器.智能指针等非常有用的东西. 平常在实际的应用中多半要么是模板函数与模板函数重载,或者是非 ...

随机推荐

  1. oracle学习篇十二:索引

    索引: 查询User_indexes可以获取有关用户已创建的索引的详细信息. 查询User_ind_partitions可以获取有关用户已创建的分区索引的详细信息. 查询User_ind_column ...

  2. Python爬虫之requests模块(1)

    一.引入 Requests 唯一的一个非转基因的 Python HTTP 库,人类可以安全享用. 警告:非专业使用其他 HTTP 库会导致危险的副作用,包括:安全缺陷症.冗余代码症.重新发明轮子症.啃 ...

  3. javascript实现数据结构与算法系列

    1.线性表(Linear list) 线性表--简单示例及线性表的顺序表示和实现 线性表--线性链表(链式存储结构) 线性表的静态单链表存储结构 循环链表与双向链表 功能完整的线性链表 线性链表的例子 ...

  4. python数据分析工具安装集合

    用python做数据分析离不开几个好的轮子(或称为科学棧/第三方包等),比如matplotlib,numpy, scipy, pandas, scikit-learn, gensim等,这些包的功能强 ...

  5. Android 从 Web 唤起 APP

    前言 知乎在手机浏览器打开,会有个 App 内打开的按钮,点击直接打开且跳转到该详情页,是不是有点神奇,是如何做到的呢? 效果预览 Uri Scheme 配置 intent-filter Androi ...

  6. SQLite入门操作(一)

    //++其他的头文件 #include "sqlite3.h" #pragma comment(lib,"sqlite3.lib") int GetItemCo ...

  7. Selenium2学习(十一)-- select下拉框

    本篇以百度设置下拉选项框为案例,详细介绍select下拉框相关的操作方法. 一.认识select    1.打开百度-设置-搜索设置界面,如下图所示 2.箭头所指位置,就是select选项框,打开页面 ...

  8. sudo cat > EOF权限问题

    sudo bash -c 'cat << EOF > /etc/yum.repos.d/some-name.repo line1 line2 line3 EOF'

  9. OC static 和变量

    #include <stdio.h> // 如果在不同源文件出现了同名的内部变量,那么这些变量将互不干扰 static int b; // 用static修饰的全部变量,可以称为内部变量 ...

  10. libevent-select模型分析

    下面内容为windows下select模型分析,原博客链接 http://blog.csdn.net/fish_55_66/article/details/50352080 https://www.c ...