ac多模式匹配 java_Aho-Corasick 多模式匹配算法、AC自动机详解

本文介绍了Aho-Corasick多模式匹配算法,通过建立Trie树并添加失败路径形成AC自动机,实现高效地在文本中查找多个模式串。给出了Java代码示例,展示了如何构建AC自动机并搜索文本。
摘要由CSDN通过智能技术生成

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树如下,其中红色标记的圈是表示为接收态:

36f67fdd73715d6c9af5c8060588a14e.png

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

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

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

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

7fd8bf842cd1852ff42399e1e93f7c45.png

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

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

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

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

3.Aho-Corasick算法代码示例

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

待匹配文本:"sdmfhsgnshejfgnihaofhsrnihao"

代码:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 #include

2 #include

3 #include

4 #include

5 using namespacestd;6

7 typedef structnode{8 struct node *next[26]; //接收的态

9 struct node *par; //父亲节点

10 struct node *fail; //失败节点

11 charinputchar;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 inti;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 returntnode;30 }31

32 /**33 将Trie树中,root节点的分支节点,放入队列34 */

35 int nodeToQueue(Tree root,queue &myqueue)36 {37 inti;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 inti,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

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 else

68 tmp1=tmp1->next[pattern[i][j]-'a'];69 }70 tmp1->patterTag=1;71 tmp1->patterNo=i;72 }73 returnroot;74 }75

76 /**77 建立失败指针78 */

79 intbuildingFailPath(Tree root)80 {81 inti;82 charinputchar;83 queuemyqueue;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,intlen)130 {131 TreeNode *tmp=root;132 int i=0;133 while(i next[pos]!=NULL)137 {138 tmp=tmp->next[pos];139 if(tmp->patterTag==1) ///如果为接收态

140 {141 cout<patterNo])+1<patterNo<patterNo]<

146 {147 if(tmp==root)148 i++;149 else

150 {151 tmp=tmp->fail;152 if(tmp->patterTag==1) //如果为接收态

153 cout<patterNo])+1<patterNo<patterNo]<fail;160 if(tmp->patterTag==1)161 cout<patterNo])+1<patterNo<patterNo]<

166 /**167 释放内存,DFS168 */

169 intdestory(Tree tree)170 {171 if(tree==NULL)172 return 0;173 queuemyqueue;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 intmain()194 {195 char a[]="sdmfhsgnshejfgnihaofhsrnihao";196 Tree root=buildingTree(); ///建立Trie树

197 buildingFailPath(root); ///添加失败转移

198 cout<

202 destory(root); ///释放动态申请内存

203 return 0;204 }

View Code

输出:

8212a058c66d962e59df8d2c042a271b.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值