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"

  代码:

  1 #include<iostream>2 #include<string.h>3 #include<malloc.h>4 #include <queue>5 using namespace std;6 7 typedef struct node{8     struct node *next[26];  //接收的态9     struct node *par;   //父亲节点10     struct node *fail;  //失败节点11     char inputchar;12     int patterTag;    //是否为可接收态13     int patterNo;   //接收态对应的可接受模式14 }*Tree,TreeNode;15 char pattern[4][30]={"nihao","hao","hs","hsr"};16 17 /**18 申请新的节点,并进行初始化19 */20 TreeNode *getNewNode()21 {22     int i;23     TreeNode* tnode=(TreeNode*)malloc(sizeof(TreeNode));24     tnode->fail=NULL;25     tnode->par=NULL;26     tnode->patterTag=0;27     for(i=0;i<26;i++)28         tnode->next[i]=NULL;29     return tnode;30 }31 32 /**33 将Trie树中,root节点的分支节点,放入队列34 */35 int  nodeToQueue(Tree root,queue<Tree> &myqueue)36 {37     int i;38     for (i = 0; i < 26; i++)39     {40         if (root->next[i]!=NULL)41             myqueue.push(root->next[i]);42     }43     return 0;44 }45 46 /**47 建立trie树48 */49 Tree buildingTree()50 {51     int i,j;52     Tree root=getNewNode();53     Tree tmp1=NULL,tmp2=NULL;54     for(i=0;i<4;i++)55     {56         tmp1=root;57         for(j=0;j<strlen(pattern[i]);j++)   ///对每个模式进行处理58         {59             if(tmp1->next[pattern[i][j]-'a']==NULL) ///是否已经有分支,Trie共用节点60             {61                 tmp2=getNewNode();62                 tmp2->inputchar=pattern[i][j];63                 tmp2->par=tmp1;64                 tmp1->next[pattern[i][j]-'a']=tmp2;65                 tmp1=tmp2;66             }67             else68                 tmp1=tmp1->next[pattern[i][j]-'a'];69         }70         tmp1->patterTag=1;71         tmp1->patterNo=i;72     }73     return root;74 }75 76 /**77 建立失败指针78 */79 int buildingFailPath(Tree root)80 {81     int i;82     char inputchar;83     queue<Tree> myqueue;84     root->fail=root;85     for(i=0;i<26;i++)   ///对root下面的第二层进行特殊处理86     {87         if (root->next[i]!=NULL)88         {89             nodeToQueue(root->next[i],myqueue);90             root->next[i]->fail=root;91         }92     }93 94     Tree tmp=NULL,par=NULL;95     while(!myqueue.empty())96     {97         tmp=myqueue.front();98         myqueue.pop();99         nodeToQueue(tmp,myqueue);
100
101         inputchar=tmp->inputchar;
102         par=tmp->par;
103
104         while(true)
105         {
106             if(par->fail->next[inputchar-'a']!=NULL)
107             {
108                 tmp->fail=par->fail->next[inputchar-'a'];
109                 break;
110             }
111             else
112             {
113                 if(par->fail==root)
114                 {
115                     tmp->fail=root;
116                     break;
117                 }
118                 else
119                     par=par->fail->par;
120             }
121         }
122     }
123     return 0;
124 }
125
126 /**
127 进行多模式搜索,即搜寻AC自动机
128 */
129 int searchAC(Tree root,char* str,int len)
130 {
131     TreeNode *tmp=root;
132     int i=0;
133     while(i < len)
134     {
135         int pos=str[i]-'a';
136         if (tmp->next[pos]!=NULL)
137         {
138             tmp=tmp->next[pos];
139             if(tmp->patterTag==1)    ///如果为接收态
140             {
141                 cout<<i-strlen(pattern[tmp->patterNo])+1<<'\t'<<tmp->patterNo<<'\t'<<pattern[tmp->patterNo]<<endl;
142             }
143             i++;
144         }
145         else
146         {
147             if(tmp==root)
148                 i++;
149             else
150             {
151                 tmp=tmp->fail;
152                 if(tmp->patterTag==1)    //如果为接收态
153                     cout<<i-strlen(pattern[tmp->patterNo])+1<<'\t'<<tmp->patterNo<<'\t'<<pattern[tmp->patterNo]<<endl;
154             }
155         }
156     }
157     while(tmp!=root)
158     {
159         tmp=tmp->fail;
160         if(tmp->patterTag==1)
161             cout<<i-strlen(pattern[tmp->patterNo])+1<<'\t'<<tmp->patterNo<<'\t'<<pattern[tmp->patterNo]<<endl;
162     }
163     return 0;
164 }
165
166 /**
167 释放内存,DFS
168 */
169 int destory(Tree tree)
170 {
171     if(tree==NULL)
172         return 0;
173     queue<Tree> myqueue;
174     TreeNode *tmp=NULL;
175
176     myqueue.push(tree);
177     tree=NULL;
178     while(!myqueue.empty())
179     {
180         tmp=myqueue.front();
181         myqueue.pop();
182
183         for (int i = 0; i < 26; i++)
184         {
185             if(tmp->next[i]!=NULL)
186                 myqueue.push(tmp->next[i]);
187         }
188         free(tmp);
189     }
190     return 0;
191 }
192
193 int main()
194 {
195     char a[]="sdmfhsgnshejfgnihaofhsrnihao";
196     Tree root=buildingTree();   ///建立Trie树
197     buildingFailPath(root); ///添加失败转移
198     cout<<"待匹配字符串:"<<a<<endl;
199     cout<<"模式"<<pattern[0]<<" "<<pattern[1]<<" "<<pattern[2]<<" "<<pattern[3]<<" "<<endl<<endl;
200     cout<<"匹配结果如下:"<<endl<<"位置\t"<<"编号\t"<<"模式"<<endl;
201     searchAC(root,a,strlen(a)); ///搜索
202     destory(root);  ///释放动态申请内存
203     return 0;
204 }

  输出:

  

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

Aho-Corasick 多模式匹配算法、AC自动机详解相关推荐

  1. 字符串处理【AC自动机】 - 原理 AC自动机详解

    字符串处理[AC自动机] - 原理 AC自动机详解 AC自动机(Aho-Corasick automaton)在1975年产生于贝尔实验室,是著名的多模匹配算法. 学习AC自动机,要有KMP和Trie ...

  2. Aho-Corasick 多模式匹配算法(AC自动机) 的算法详解及具体实现

    多模式匹配 多模式匹配就是有多个模式串P1,P2,P3-,Pm,求出所有这些模式串在连续文本T1-.n中的所有可能出现的位置. 例如:求出模式集合{"nihao","ha ...

  3. [数据结构]模式匹配算法--KMP算法详解

    目录 一. 模式匹配 二. 模式匹配算法 1. 朴素模式匹配算法 2. KMP算法 1). KMP算法的优势 2). KMP算法的原理 3). next数组的构造 4). 利用next数组匹配的过程 ...

  4. android doze模式源码分析,Android Doze模式启用和恢复详解

    从Android 6.0(API level 23)开始,Android提出了两个延长电池使用时间的省电特性给用户.用户管理可以在没有充电的情况下管理app的行为.当用户一段时间没有使用手机的时候,D ...

  5. python的编程模式-Python设计模式之状态模式原理与用法详解

    本文实例讲述了Python设计模式之状态模式原理与用法.分享给大家供大家参考,具体如下: 状态模式(State Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类 ...

  6. 深入剖析Redis系列(三) - Redis集群模式搭建与原理详解

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

  7. 怎样进入android模式,安卓手机如何进入Recovery模式的通用方式详解

    2014-12-12 15:24:16 安卓手机如何进入Recovery模式的通用方式详解 标签:安卓 Recovery模式 教程 Recovery模式是什么?这里说的Recovery模式主要指的是安 ...

  8. Java单机部署,Nacos docker单机模式部署实现过程详解

    Nacos 的部署,我使用的时docker 部署(单机模式 mysql),官网文档:https://nacos.io/zh-cn/docs/quick-start-docker.html 拉取代码: ...

  9. 设计模式 - 抽象工厂模式(abstract factory pattern) 详解

    抽象工厂模式(abstract factory pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy/article/details/270916 ...

最新文章

  1. 漫画:什么是计数排序?
  2. 如何将商业第三方文物整合到您的Maven版本中
  3. 【MySQL】基于MySQL的SQL增删改查实战演练
  4. CSM管理系统_复选框删除多条记录
  5. 人工智能和分布式账本如何帮助我们解决假新闻
  6. iOS UIPageControl
  7. 损失函数的“噪音免疫力”
  8. centos 卸载 jdk
  9. 2022年第十三届蓝桥杯省赛--难度评价
  10. imx6ul pinctrl 驱动浅析
  11. python 打包exe_python打包exe能运行但是没有结果解决方案
  12. 摄影测量内定向编程实现
  13. 146条经典偏方(祖传秘方)
  14. C#基础 连接数据库
  15. PgAdmin出现Utility file not found. Please correct the Binary Path in the Preferences dialog的解决办法
  16. Smart Thief 问题
  17. Mob平台获取手机验证码
  18. 微信小程序跳转美团外卖小程序时出现白屏解决demo
  19. 一个假猪套神器:NET CAT-NC
  20. Android高版本联网失败报错:Cleartext HTTP traffic to xxx not permitted解决方法

热门文章

  1. 大学生计算机基础大难,大学生计算机基础实训六样文
  2. Ubuntu 18.04 U盘启动安装教程【图文教程,非常详细!!!!】
  3. PO1382(贪心)
  4. js将当前时间格式化为年-月-日 时:分:秒
  5. web前端作业--响应式美食菜谱网页设计(HTML+CSS+JavaScript+)实现
  6. Chapter 5. Monte Carlo Methods
  7. 用户日活月活怎么统计 - Redis HyperLogLog 详解
  8. 如何通过二极管设计一个与门电路
  9. Latex修改局部字体大小
  10. 以终为始,向死而生——5月份英语总结