议题: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)的更多相关文章

  1. 前端如何应对笔试算法题?(用node编程)

    用nodeJs写算法题 咱们前端使用算法的地方不多,但是为了校招笔试,不得不针对算法题去练习呀! 好不容易下定决心 攻克算法题.发现js并不能像c语言一样自建输入输出流.只能回去学习c语言了吗?其实不 ...

  2. UVA11019 Matrix Matcher【hash傻逼题】【AC自动机好题】

    LINK1 LINK2 题目大意 让你在一个大小为\(n*m\)的矩阵中找大小是\(x*y\)的矩阵的出现次数 思路1:Hash hash思路及其傻逼 你把一维情况扩展一下 一维是一个bas,那你二维 ...

  3. (转)两种高效过滤敏感词算法--DFA算法和AC自动机算法

    原文:https://blog.csdn.net/u013421629/article/details/83178970 一道bat面试题:快速替换10亿条标题中的5万个敏感词,有哪些解决思路? 有十 ...

  4. 「刷题笔记」AC自动机

    自动AC机 Keywords Research 板子题,同luoguP3808,不过是多测. 然后多测不清空,\(MLE\)两行泪. 板子放一下 #include<bits/stdc++.h&g ...

  5. 笔试算法题(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 ...

  6. 笔试算法题(50):简介 - 广度优先 & 深度优先 & 最小生成树算法

    广度优先搜索&深度优先搜索(Breadth First Search & Depth First Search) BFS优缺点: 同一层的所有节点都会加入队列,所以耗用大量空间: 仅能 ...

  7. 笔试算法题(48):简介 - A*搜索算法(A Star Search Algorithm)

    A*搜索算法(A Star Search Algorithm) A*算法主要用于在二维平面上寻找两个点之间的最短路径.在从起始点到目标点的过程中有很多个状态空间,DFS和BFS没有任何启发策略所以穷举 ...

  8. 笔试算法题(44):简介 - 动态规划(Dynamic Programming)

    议题:动态规划(Dynamic Programming) 分析: DP主要用于解决包含重叠子问题(Overlapping Subproblems)的最优化问题,其基本策略是将原问题分解为相似的子问题, ...

  9. 笔试算法题(46):简介 - 二叉堆 & 二项树 & 二项堆 & 斐波那契堆

    二叉堆(Binary Heap) 二叉堆是完全二叉树(或者近似完全二叉树):其满足堆的特性:父节点的值>=(<=)任何一个子节点的键值,并且每个左子树或者右子树都是一 个二叉堆(最小堆或者 ...

随机推荐

  1. 使用Asp.net Identity 创建用户 、登录代码

    1.Identity 1中的注册.登录.注销代码 vs 2013中自带的注册用户代码: 1.首先创建一个ApplicationUser 初始化用户名. 2.使用UserManager创建一个用户,用使 ...

  2. MySQL之不得不说的keepsync和trysync

    此文已由作者温正湖授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 开宗明义,keepsync和trysync是网易MySQL分支版本InnoSQL的两个参数,非常重要的两个参 ...

  3. 【插件开发】—— 7 SWT布局详解,不能再详细了!

    前文回顾: 1 插件学习篇 2 简单的建立插件工程以及模型文件分析 3 利用扩展点,开发透视图 4 SWT编程须知 5 SWT简单控件的使用与布局搭配 6 SWT复杂空间与布局搭配 前面几篇都提到了S ...

  4. 倒排索引构建算法BSBI和SPIMI

    参考:https://blog.csdn.net/androidlushangderen/article/details/44889677 倒排索引 : 一般的索引检索信息的方式.比如原始的数据源假设 ...

  5. Vue的响应式原理

    Vue的响应式原理 一.响应式的底层实现 1.Vue与MVVM Vue是一个 MVVM框架,其各层的对应关系如下 View层:在Vue中是绑定dom对象的HTML ViewModel层:在Vue中是实 ...

  6. POJ 1686 Lazy Math Instructor(栈)

    原题目网址:http://poj.org/problem?id=1686 题目中文翻译: Description 数学教师懒得在考卷中给一个问题评分,因为这个问题中,学生会为所问的问题提出一个复杂的公 ...

  7. Python测试工具——nose

    1.nose 特点: a)         自动发现测试用例(包含[Tt]est文件以及文件包中包含test的函数) b)         以test开头的文件 c)         以test开头的 ...

  8. asp.net core连接sqlserver

    开发环境:win7,vs2017,sqlserver2014 vs上建立一个asp.net core web项目和一个.net core的类库项目DBA 简单起见,在DBA项目中就一个类SqlServ ...

  9. string.Format 中不能包含{}字符串

    string scss = @"<style type=""text/css""> body{ margin-left: {0}px; m ...

  10. 轻松搞懂Java中的自旋锁

    前言 在之前的文章<一文彻底搞懂面试中常问的各种“锁”>中介绍了Java中的各种“锁”,可能对于不是很了解这些概念的同学来说会觉得有点绕,所以我决定拆分出来,逐步详细的介绍一下这些锁的来龙 ...