议题: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. jQuery入坑指南

    前言 Ajax官方文档 爱jQuery jQuery插件库 jQuery中文api input 赋值和取值 记录一下: 在写一个input赋值,二话不说就直接利用了$('#xx').val()来进行取 ...

  2. bzoj 1787: [Ahoi2008]Meet 紧急集合【树链剖分lca】

    对于三个点求最小路径长度和,答案肯定在某两个点的lca上,因为如果把集合点定在公共lca上,一定有两个点汇合后再一起上到lca,这样显然不如让剩下的那个点下来 这个lca可能是深度最深的--但是我懒得 ...

  3. bzoj 1306: [CQOI2009]match循环赛【dfs+剪枝】

    大力剪枝,最后洛谷上还开了o2才过-- 大概这样剪枝: 1.搜索中,一个队当前得分超过要求或者一个队剩下的比赛场数全赢也达不到要求则return: 2.注意到如果平局,最总分的贡献是2,否则是3,所以 ...

  4. Python网络爬虫与信息提取

    1.Requests库入门 Requests安装 用管理员身份打开命令提示符: pip install requests 测试:打开IDLE: >>> import requests ...

  5. Python圈中的符号计算库-Sympy(转载)

    <本文来自公众号“大邓带你玩python”,转载> import math math.sqrt(8) 2.8284271247461903 我们看看Python中结果 math.sqrt( ...

  6. PowerDesigner在PDM转换为sql脚本时报错Generation aborted due to errors detected during the verification of the mod

    在设计概念数据模型(CDM)之后,转换为物理数据模型(PDM),之后转换为sql脚本时报错Generation aborted due to errors detected during the ve ...

  7. KMP HDOJ 4300 Clairewd's message

    题目传送门 题意:完全不懂,最后还是看题解才理解了.第一行字符串是密文变成明文的规则,比如第二个样例:“qwertyuiopasdfghjklzxcvbnm”,‘q'对应的明文为’a','w'对应'b ...

  8. jmeter(七)函数

    JMeter函数是一些能够转化在测试树中取样器或者其他配置元件的域的特殊值.一个函数的调用就像这样:${_functionName(var1,var2,var3)},-functionName匹配函数 ...

  9. 445 Add Two Numbers II 两数相加 II

    给定两个非空链表来代表两个非负整数.数字最高位位于链表开始位置.它们的每个节点只存储单个数字.将这两数相加会返回一个新的链表.你可以假设除了数字 0 之外,这两个数字都不会以零开头.进阶:如果输入链表 ...

  10. 在linux环境下使用itext生成pdf

    转载请注明出处 https://www.cnblogs.com/majianming/p/9537173.html 项目中需要对订单生成pdf文件,在不断的尝试之后,终于生成了比较满意的pdf文档. ...