议题: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. Android中string.xml中的的标签xliff:g(转载)

    转自:http://blog.csdn.net/xuewater/article/details/25687987 在资源文件中写字符串时,如果这个字符串时动态的,又不确定的值在里面,我们就可以用xl ...

  2. E20180225-hm-xa

    variation  n. 变化,变动; 变异,演变; 变奏曲; 变量; auxiliary adj. 辅助的 subscript  adj. 下标的,写在下方的,脚注的;  n. 下标,脚注,下角数 ...

  3. E201700525-hm

    skeleton n. 骨骼; (建筑物等的) 骨架; 梗概; 骨瘦如柴的人(或动物);adj. 骨骼的; 骨瘦如柴的; 概略的; 基本的; cloud   n. 云; 云状物;  invoke   ...

  4. Ruby自定义打印的字符串

    重要性就不说了,没了这个出点小bug都要查半天. def inspect()     return "要输出的字符串写在这里咯" end         实际代码 #------- ...

  5. bzoj 3196 Tyvj 1730 二逼平衡树【线段树 套 splay】

    四舍五入就是个暴力. 对于线段树的每个区间都开一棵按权值排序的splay 对于第二个操作,二分一下,每次查询mid的排名,复杂度 $ O(nlog(n)^{3}) $ 其余的操作都是$ O(nlog( ...

  6. _bzoj1051 [HAOI2006]受欢迎的牛【强联通】

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1051 保存tarjan模版. 求强联通分量,缩点. #include <cstdio& ...

  7. DP(两次) UVA 10163 Storage Keepers

    题目传送门 /* 题意:(我懒得写,照搬网上的)有n个仓库,m个人看管.一个仓库只能由一个人来看管,一个人可以看管多个仓库. 每个人有一个能力值pi,如果他看管k个仓库,那么所看管的每个仓库的安全值为 ...

  8. Service官方教程(2)*IntentService与Service示例、onStartCommand()3个返回值的含义。

    1.Creating a Started Service A started service is one that another component starts by calling start ...

  9. HttpURLConnection教程

    1.Class Overview An URLConnection for HTTP (RFC 2616) used to send and receive data over the web. Da ...

  10. 《effective java》中文第2版 note

    ,第15条[66]: 为不可变类提供静态工厂, eg : Integer/BigInteger 使用了静态工厂缓存了一些常用的实例, 通常 Integer -128 ~ +127. BigIntege ...