Aho-Corasick算法是多模式匹配中的经典算法,目前在实际应用中较多。

Aho-Corasick算法对应的数据结构是Aho-Corasick自动机,简称AC自动机。

搞编程的一般都应该知道自动机FA吧,具体细分为:确定性有限状态自动机(DFA)和非确定性有限状态自动机NFA。普通的自动机不能进行多模式匹配,AC自动机增加了失败转移,转移到已经输入成功的文本的后缀,来实现。

1.多模式匹配

  多模式匹配就是有多个模式串P1,P2,P3...,Pm,求出所有这些模式串在连续文本T1....n中的所有可能出现的位置。

  例如:求出模式集合{"nihao","hao","hs","hsr"}在给定文本"sdmfhsgnshejfgnihaofhsrnihao"中所有可能出现的位置

2.Aho-Corasick算法  

  使用Aho-Corasick算法需要三步:

  1.建立模式的Trie

  2.给Trie添加失败路径

  3.根据AC自动机,搜索待处理的文本

  下面说明这三步:

2.1建立多模式集合的Trie

  Trie树也是一种自动机。对于多模式集合{"say","she","shr","he","her"},对应的Trie树如下,其中红色标记的圈是表示为接收态:

  

2.2为多模式集合的Trie树添加失败路径,建立AC自动机

  构造失败指针的过程概括起来就一句话:设这个节点上的字母为C,沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也有字母为C的节点。然后把当前节点的失败指针指向那个字母也为C的儿子。如果一直走到了root都没找到,那就把失败指针指向root。

  使用广度优先搜索BFS,层次遍历节点来处理,每一个节点的失败路径。  

  特殊处理:第二层要特殊处理,将这层中的节点的失败路径直接指向父节点(也就是根节点)

2.3根据AC自动机,搜索待处理的文本

  从root节点开始,每次根据读入的字符沿着自动机向下移动。

  当读入的字符,在分支中不存在时,递归走失败路径。如果走失败路径走到了root节点,则跳过该字符,处理下一个字符。

  因为AC自动机是沿着输入文本的最长后缀移动的,所以在读取完所有输入文本后,最后递归走失败路径,直到到达根节点,这样可以检测出所有的模式。

3.Aho-Corasick算法代码示例

  模式串集合:{"nihao","hao","hs","hsr"}

  待匹配文本:"sdmfhsgnshejfgnihaofhsrnihao"

  代码:

 #include<iostream>
#include<string.h>
#include<malloc.h>
#include <queue>
using namespace std; typedef struct node{
struct node *next[]; //接收的态
struct node *par; //父亲节点
struct node *fail; //失败节点
char inputchar;
int patterTag; //是否为可接收态
int patterNo; //接收态对应的可接受模式
}*Tree,TreeNode;
char pattern[][]={"nihao","hao","hs","hsr"}; /**
申请新的节点,并进行初始化
*/
TreeNode *getNewNode()
{
int i;
TreeNode* tnode=(TreeNode*)malloc(sizeof(TreeNode));
tnode->fail=NULL;
tnode->par=NULL;
tnode->patterTag=;
for(i=;i<;i++)
tnode->next[i]=NULL;
return tnode;
} /**
将Trie树中,root节点的分支节点,放入队列
*/
int nodeToQueue(Tree root,queue<Tree> &myqueue)
{
int i;
for (i = ; i < ; i++)
{
if (root->next[i]!=NULL)
myqueue.push(root->next[i]);
}
return ;
} /**
建立trie树
*/
Tree buildingTree()
{
int i,j;
Tree root=getNewNode();
Tree tmp1=NULL,tmp2=NULL;
for(i=;i<;i++)
{
tmp1=root;
for(j=;j<strlen(pattern[i]);j++) ///对每个模式进行处理
{
if(tmp1->next[pattern[i][j]-'a']==NULL) ///是否已经有分支,Trie共用节点
{
tmp2=getNewNode();
tmp2->inputchar=pattern[i][j];
tmp2->par=tmp1;
tmp1->next[pattern[i][j]-'a']=tmp2;
tmp1=tmp2;
}
else
tmp1=tmp1->next[pattern[i][j]-'a'];
}
tmp1->patterTag=;
tmp1->patterNo=i;
}
return root;
} /**
建立失败指针
*/
int buildingFailPath(Tree root)
{
int i;
char inputchar;
queue<Tree> myqueue;
root->fail=root;
for(i=;i<;i++) ///对root下面的第二层进行特殊处理
{
if (root->next[i]!=NULL)
{
nodeToQueue(root->next[i],myqueue);
root->next[i]->fail=root;
}
} Tree tmp=NULL,par=NULL;
while(!myqueue.empty())
{
tmp=myqueue.front();
myqueue.pop();
nodeToQueue(tmp,myqueue); inputchar=tmp->inputchar;
par=tmp->par; while(true)
{
if(par->fail->next[inputchar-'a']!=NULL)
{
tmp->fail=par->fail->next[inputchar-'a'];
break;
}
else
{
if(par->fail==root)
{
tmp->fail=root;
break;
}
else
par=par->fail->par;
}
}
}
return ;
} /**
进行多模式搜索,即搜寻AC自动机
*/
int searchAC(Tree root,char* str,int len)
{
TreeNode *tmp=root;
int i=;
while(i < len)
{
int pos=str[i]-'a';
if (tmp->next[pos]!=NULL)
{
tmp=tmp->next[pos];
if(tmp->patterTag==) ///如果为接收态
{
cout<<i-strlen(pattern[tmp->patterNo])+<<'\t'<<tmp->patterNo<<'\t'<<pattern[tmp->patterNo]<<endl;
}
i++;
}
else
{
if(tmp==root)
i++;
else
{
tmp=tmp->fail;
if(tmp->patterTag==) //如果为接收态
cout<<i-strlen(pattern[tmp->patterNo])+<<'\t'<<tmp->patterNo<<'\t'<<pattern[tmp->patterNo]<<endl;
}
}
}
while(tmp!=root)
{
tmp=tmp->fail;
if(tmp->patterTag==)
cout<<i-strlen(pattern[tmp->patterNo])+<<'\t'<<tmp->patterNo<<'\t'<<pattern[tmp->patterNo]<<endl;
}
return ;
} /**
释放内存,DFS
*/
int destory(Tree tree)
{
if(tree==NULL)
return ;
queue<Tree> myqueue;
TreeNode *tmp=NULL; myqueue.push(tree);
tree=NULL;
while(!myqueue.empty())
{
tmp=myqueue.front();
myqueue.pop(); for (int i = ; i < ; i++)
{
if(tmp->next[i]!=NULL)
myqueue.push(tmp->next[i]);
}
free(tmp);
}
return ;
} int main()
{
char a[]="sdmfhsgnshejfgnihaofhsrnihao";
Tree root=buildingTree(); ///建立Trie树
buildingFailPath(root); ///添加失败转移
cout<<"待匹配字符串:"<<a<<endl;
cout<<"模式"<<pattern[]<<" "<<pattern[]<<" "<<pattern[]<<" "<<pattern[]<<" "<<endl<<endl;
cout<<"匹配结果如下:"<<endl<<"位置\t"<<"编号\t"<<"模式"<<endl;
searchAC(root,a,strlen(a)); ///搜索
destory(root); ///释放动态申请内存
return ;
}

  输出:

  

(上面的两个图,参考网页:http://www.cppblog.com/mythit/archive/2009/04/21/80633.html

Aho-Corasick 多模式匹配算法、AC自动机详解的更多相关文章

  1. [转] AC自动机详解

    转载自:http://hi.baidu.com/nialv7/item/ce1ce015d44a6ba7feded52d AC自动机详解 AC自动机是用来处理多串匹配问题的,即给你很多串,再给你一篇文 ...

  2. AC自动机详解

    概述 AC自动机全称Aho-Corasick automaton,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法. 考虑这样一个场景,给出L个模式字符串(加总长度为N),以及长度为M大文本, ...

  3. AC自动机详解 (P3808 模板)

    AC自动机笔记 0.0 前言 哇,好久之前就看了 KMP 和 Trie 树,但是似乎一直没看懂 AC自动机?? 今天灵光一闪,加上之前看到一些博客和视频,瞬间秒懂啊... 其实这个玩意还是蛮好理解的. ...

  4. AC自动机详解(附加可持久化AC自动机)

    AC自动机 AC自动机,说白了就是在trie树上跑kmp(其实个人感觉比kmp容易理解).是一种多匹配串,单个主串的匹配.概括来说,就是将多个匹配串构造一个trie树,对于每个trie树的节点构造nx ...

  5. BS模式的模型结构详解

    编号:1004时间:2016年4月12日16:59:17功能:BS模式的模型结构详解 URL:http://blog.csdn.net/icerock2000/article/details/4000 ...

  6. Vmware在NAT模式下网络配置详解

    Vmware在NAT模式下网络配置详解 Linux中的网络配置对于接触Linux不久的小白菜来说,还是小有难度的,可能是不熟悉这种与windows系列迥然不同的命令行操作,也可能是由于对Linux的结 ...

  7. 【转】AC算法详解

    原文转自:http://blog.csdn.net/joylnwang/article/details/6793192 AC算法是Alfred V.Aho(<编译原理>(龙书)的作者),和 ...

  8. 深入剖析Redis系列: Redis集群模式搭建与原理详解

    前言 在 Redis 3.0 之前,使用 哨兵(sentinel)机制来监控各个节点之间的状态.Redis Cluster 是 Redis 的 分布式解决方案,在 3.0 版本正式推出,有效地解决了 ...

  9. 各个模式的accesstoken续期详解

    一些预备知识 jwt的时间格式 转换为时间可以用js, new Date(1531841745*1000) ==>Tue Jul 17 2018 23:35:45 GMT+0800 (中国标准时 ...

随机推荐

  1. Java 8 新特性---------Stream

    Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据. Stream使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象 ...

  2. c#版flappybird 未完全实现

    这些天开始在深圳找工作,想着把从前有些淡忘的技术再温故下.看到尊敬的<传智播客>有一期公开课,讲的是用c#编写flappybird小游戏,也就自己搜了下游戏资源,也来试试看. 其实用到的技 ...

  3. 《Linux内核分析》第一周——计算机是如何工作的?

    杨舒雯 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 课程内容 1.诺曼依体系 ...

  4. Alpha版阶段项目总结

    一,设想和目标 1. 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 针对铁道大学大学生,增加他们的社交范围.我们的软件定义很清楚.对典型用户和典型场景有清晰的描述 ...

  5. 在 Ubuntu16.04 中搭建 Spark 单机开发环境 (JDK + Scala + Spark)

    1.准备 本文主要讲述如何在Ubuntu 16.04 中搭建 Spark 2.11 单机开发环境,主要分为 3 部分:JDK 安装,Scala 安装和 Spark 安装. JDK 1.8:jdk-8u ...

  6. thinkphp5报错

    thinkPHP5配置nginx环境无法打开(require():open_basedirrestrictionineffect.File(/mnt/hgfs/ro (2018-07-19 22:05 ...

  7. 普通的jdbc事务在插入数据后 下面的代码报错时 数据不会回滚 但是 spring的事务会回滚

    普通的jdbc事务在插入数据后 下面的代码报错时 数据不会回滚 但是 spring的事务会回滚

  8. codeforces71A

    Way Too Long Words CodeForces - 71A XUPT_ACM的杨队是一个强迫症晚期的大神,他特别反感长单词,就像 "localization" 和&qu ...

  9. BZOJ5287 HNOI2018毒瘤(虚树+树形dp)

    显然的做法是暴力枚举非树边所连接两点的选或不选,大力dp.考场上写的是最暴力的O(3n-mn),成功比大众分少10分.容斥或者注意到某些枚举是不必要的就能让底数变成2.但暴力的极限也就到此为止. 每次 ...

  10. IDEA中新建Module时Module name、Content root、Module file location的含义

    如下图测试: 最开始默认情况下,Content root.Module file location两行,最末尾的数据跟Module name是相同的. 现在对三行数据,强制进行不同的命名,Finish ...