笔试算法题(45):简介 - AC自动机(Aho-Corasick Automation)
议题:AC自动机(Aho-Corasick Automation)
分析:
此算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一;一个常见的例子就是给定N个单词,给定包含M个字符的文章,要求确定多少个给定的单词在文章中出现过;AC自动机在匹配文本时不需要回溯,处理时间复杂度与pattern无关,仅是target的长度O(N);构建AC自动机的时间复杂度;
与KMP算法类似,AC自动机也是利用前一个匹配模式串失效之后得到的信息来确定下一个匹配的开始位置,从而避免回移主串的匹配指针;与KMP算法不同的 是,AC自动机是针对多个模式串在同一个主串上的匹配,不仅在同一个模式串匹配内部,而且在不同的模式串之间利用前一次匹配失效的信息来确定下一次匹配的 开始位置;

AC自动机的构建主要由三个步骤:
针对所有模式串构建Trie树;
针对所有Trie树上的接点构建Fail指针:Fail指针指向的是如果当前节点匹配失败,则从通过Fail指针指向的新的节点开始匹配,但新的节点必须满足所在在新节点模式串的前缀必须是转移前的节点所在模式串的子串,也就是已经匹配成功的部分;
正式匹配过程:将主串在Trie树上匹配,主要有两种操作,如果当前节点匹配成功,则随机选择一条子路径到达的节点;如果当前节点匹配失败,则使用Fail指针转移到新的节点;知道文本末尾;
样例:
/**
* 定义Trie树中子节点的最大个数,26个英文字母
* */
const int MAX_NUM=;
/**
* 用于构造Fail指针的队列,队列元素为拥有三个数据域
* 的对象:
* fail表示Fail指针
* Child数组表示26个子节点指针
* IsOver表示当前节点是否为一个单词的结束节点
* */
struct Node {
Node *fail;
Node *Child[MAX_NUM];
int IsOver;
Node() {
fail=NULL;
IsOver=;
for(int i=;i<;i++)
Child[i]=NULL;
}
} *queue[];
/**
* pattern表示输入的单词
* target表示目标匹配文本
* */
char pattern[];
char target[]; /**
* head表示队列queue中的头结点,新元素入队的位置
* tail表示队列queue中的尾节点,元素出队的位置
* */
int head;
int tail; /**
* 首先利用pattern构建Trie树
* Trie树的根节点root是一个包含空字符的辅助节点;
* pointer指针遍历遍历Trie树
* temp指针临时替换一个节点的26个子节点中的一个
* index指针索引一个pattern中的字符
* 如果有多个pattern字符串,则需要多次调用此方法
* */
void ConstructTrieTree(char *pattern, Node *root) {
Node *pointer=root;
Node *temp;
char *index=pattern;
/**
* 此处循环以需要插入Trie树的pattern字符串作为依据
* */
while(*index!='\0') {
temp=pointer->Child[*index-'a'];
/**
* 如果某一个字符对应的指针为NULL,则创建对应的节点
* */
if(temp==NULL)
temp=new Node();
pointer=temp;
index++;
}
/**
* 当index指针pattern末尾的时候循环结束;
* 此时pointer指向pattern的最后一个字符所在的节点
* */
pointer->IsOver=;
} /**
* 然后构造Fail指针:
* 在Trie树上构建Fail指针的基本策略如同BFS遍历
* 初始化将root入队,然后进入循环,循环检查队列是否为空,如果非空,则
* 取出一个节点进行处理,并将该节点的所有子节点加入队列;如果队列为空,
* 则算法结束;
* 对节点进行处理就是寻找其Fail指针的指向位置
* 如果当前节点的字符为A,则跳转到当前节点的父节点的fail指针指向的节点
* 处,判断其儿子中是否有为A字符的节点,如果没有则继续按照其自身的fail
* 指针指向的节点跳转,直到找到字符为A的节点或者到达root节点处
* */
void ConstructFailPointer(Node *root, Node **queue) {
/**
* 初始化root节点的fail指针,并将其入队queue
* */
head=;tail=;
root->fail=NULL;
queue[head++]=root; Node *temp;
Node *index;
while(head!=tail) {
/**
* 从队列末尾获取一个节点
* */
temp=queue[tail++];
index=NULL;
for(int i=;i<;i++) {
/**
* 处理当前节点,遍历其Child指针数组
* */
if(temp->Child[i]!=NULL) {
/**
* 对于root节点的直接子节点而言,由于他们没有
* 任何前缀而言,所以其fail指针都指向root节点
* */
if(temp==root)
temp->Child[i]->fail=root;
else {
index=temp->fail;
while(index!=NULL) {
/**
* 循环访问当前节点的fail指针指向的节点
* 考察其子节点中的Child[i]是否存在,也就是
* 是否有相同的字符
* */
if(index->Child[i]!=NULL) {
/**
* Child[i]表示当前处理的字符子节点
* temp表示原始父亲节点
* index表示根据fail指针跳转的节点
* */
temp->Child[i]->fail=index->Child[i];
break;
}
index=index->fail;
}
/**
* root节点的fail指针初始化为NULL
* */
if(index==NULL)
temp->Child[i]->fail=root;
}
/**
* 将temp节点的子节点入队queue
* */
queue[head++]=temp->Child[i];
}
}
}
} /**
* 最后正式施行字符串匹配操作:
* 利用Trie树(已经将所有的Pattern串插入)与target文本串进行匹配,有两种情况:
* 1. 如果某一个节点的Child[i]对应target的字符存在,则下到子节点
* 2. 如果某一个节点的所有子节点Child[i]都没有与target的字符匹配的,则使用fail跳转
* 匹配函数最终返回pattern数组中单词在target文本串中出现的个数
* */
int MultiPatternSearch(Node *root, char *target) {
char *index1=target;
Node *index2=root;
Node *temp;
int i=, count=;
while(index1[i]!='\0') {
/**
* 如果不能匹配则跳转到fail指针指向的节点
* */
while(index2->Child[index1[i]-'a']==NULL && index2!=root)
index2=index2->fail;
/**
* 准备匹配下一个字符,但如果上述循环是因为后面一个条件结束,则index2
* 需要重新指向root
* */
index2=index2->Child[index1[i]-'a'];
if(index2==NULL)
index2=root;
temp=index2;
while(temp!=root && temp->IsOver==) {
count++;
/**
* 如果Trie树中的一个pattern成功匹配,则需要
* 将其IsOver域设置成0,防止重复匹配;
* 然后跳转到与其具有公共前缀的fail指针指向的节点
* 继续进行匹配
* */
temp->IsOver=;
temp=temp->fail;
}
i++;
}
return count;
}
参考链接:
http://www.cppblog.com/mythit/archive/2009/04/21/80633.html
http://www.zhiwenweb.cn/Category/Security/1274.htm
笔试算法题(45):简介 - AC自动机(Aho-Corasick Automation)的更多相关文章
- 前端如何应对笔试算法题?(用node编程)
用nodeJs写算法题 咱们前端使用算法的地方不多,但是为了校招笔试,不得不针对算法题去练习呀! 好不容易下定决心 攻克算法题.发现js并不能像c语言一样自建输入输出流.只能回去学习c语言了吗?其实不 ...
- UVA11019 Matrix Matcher【hash傻逼题】【AC自动机好题】
LINK1 LINK2 题目大意 让你在一个大小为\(n*m\)的矩阵中找大小是\(x*y\)的矩阵的出现次数 思路1:Hash hash思路及其傻逼 你把一维情况扩展一下 一维是一个bas,那你二维 ...
- (转)两种高效过滤敏感词算法--DFA算法和AC自动机算法
原文:https://blog.csdn.net/u013421629/article/details/83178970 一道bat面试题:快速替换10亿条标题中的5万个敏感词,有哪些解决思路? 有十 ...
- 「刷题笔记」AC自动机
自动AC机 Keywords Research 板子题,同luoguP3808,不过是多测. 然后多测不清空,\(MLE\)两行泪. 板子放一下 #include<bits/stdc++.h&g ...
- 笔试算法题(52):简介 - KMP算法(D.E. Knuth, J.H. Morris, V.R. Pratt Algorithm)
议题:KMP算法(D.E. Knuth, J.H. Morris, V.R. Pratt Algorithm) 分析: KMP算法用于在一个主串中找出特定的字符或者模式串.现在假设主串为长度n的数组T ...
- 笔试算法题(50):简介 - 广度优先 & 深度优先 & 最小生成树算法
广度优先搜索&深度优先搜索(Breadth First Search & Depth First Search) BFS优缺点: 同一层的所有节点都会加入队列,所以耗用大量空间: 仅能 ...
- 笔试算法题(48):简介 - A*搜索算法(A Star Search Algorithm)
A*搜索算法(A Star Search Algorithm) A*算法主要用于在二维平面上寻找两个点之间的最短路径.在从起始点到目标点的过程中有很多个状态空间,DFS和BFS没有任何启发策略所以穷举 ...
- 笔试算法题(44):简介 - 动态规划(Dynamic Programming)
议题:动态规划(Dynamic Programming) 分析: DP主要用于解决包含重叠子问题(Overlapping Subproblems)的最优化问题,其基本策略是将原问题分解为相似的子问题, ...
- 笔试算法题(46):简介 - 二叉堆 & 二项树 & 二项堆 & 斐波那契堆
二叉堆(Binary Heap) 二叉堆是完全二叉树(或者近似完全二叉树):其满足堆的特性:父节点的值>=(<=)任何一个子节点的键值,并且每个左子树或者右子树都是一 个二叉堆(最小堆或者 ...
随机推荐
- ubuntu 12.04 samba 配置(转载)
转自:http://blog.sina.com.cn/s/blog_a5b607f801013av9.html 首先当然是要安装samba了,呵呵: 代码: sudo apt-get install ...
- mysql 状态查询
select COUNT(case when info.State = '0' then State end ) as daichuliCount, COUNT(case when info.St ...
- Luogu P1280 Niko的任务【线性dp】By cellur925
Nikonikoni~~ 题目传送门 这是当时学长讲dp的第一道例题,我还上去献了个丑,然鹅学长讲的方法我似董非董(??? 我当时说的怎么设计这道题的状态,但是好像说的是二维,本题数据范围均在1000 ...
- #define及其用法
#define 在#define中,标准只定义了#和##两种操作.#用来把参数转换成字符串,##则用来连接前后两个参数,把它们变成一个字符 串. #include<stdio.h ...
- (转载)Python一篇学会多线程
Python 一篇学会多线程 链接:https://www.cnblogs.com/yeayee/p/4952022.html 多线程和多进程是什么自行google补脑,廖雪峰官网也有,但是不够简洁 ...
- [ZJOI2011]道馆之战
Description 口袋妖怪(又名神奇宝贝或宠物小精灵)红/蓝/绿宝石中的水系道馆需要经过三个冰地才能到达馆主的面前,冰地中的每一个冰块都只能经过一次.当一个冰地上的所有冰块都被经过之后,到下一个 ...
- [USACO 2011 Dec Gold] Cow Calisthenics【二分】
Problem 1: Cow Calisthenics [Michael Cohen, 2010] Farmer John continues his never-ending quest to ke ...
- QT5每日一学(一)下载与安装
一.Qt SDK的下载和安装 1.下载 Qt官网主页提供了最新版Qt的下载,不过我们更倾向于去资源下载页面(https://download.qt.io/official_release ...
- 暴力(判凸四边形) FZOJ 2148 Moon Game
题目传送门 题意:给了n个点的坐标,问能有几个凸四边形 分析:数据规模小,直接暴力枚举,每次四个点判断是否会是凹四边形,条件是有一个点在另外三个点的内部,那么问题转换成判断一个点d是否在三角形abc内 ...
- 一个DBA萌新的烦恼
莫名其妙也好机缘巧合也罢,现在我成为了一名MySQL DBA. 为什么: 1.为什么leader让我转到DBA? 首先,我本身学习运维管理的时候就接触过数据库(mysql,redis),算是自身的优势 ...