﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-yefeng-随笔分类-算法</title><link>http://www.cppblog.com/yefeng/category/12510.html</link><description>夜风'blog</description><language>zh-cn</language><lastBuildDate>Sun, 02 Dec 2012 23:44:15 GMT</lastBuildDate><pubDate>Sun, 02 Dec 2012 23:44:15 GMT</pubDate><ttl>60</ttl><item><title>min(x,y)高效算法</title><link>http://www.cppblog.com/yefeng/archive/2011/08/22/154091.html</link><dc:creator>夜风</dc:creator><author>夜风</author><pubDate>Mon, 22 Aug 2011 15:58:00 GMT</pubDate><guid>http://www.cppblog.com/yefeng/archive/2011/08/22/154091.html</guid><wfw:comment>http://www.cppblog.com/yefeng/comments/154091.html</wfw:comment><comments>http://www.cppblog.com/yefeng/archive/2011/08/22/154091.html#Feedback</comments><slash:comments>16</slash:comments><wfw:commentRss>http://www.cppblog.com/yefeng/comments/commentRss/154091.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yefeng/services/trackbacks/154091.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 今天偶然看到一个讲<a href="http://www.cppblog.com/Cunch/archive/2011/08/20/153983.html#Post">求较小值的帖子</a>，让我突然想起一年前一次折腾逆向工程的尝试，当时用IDA进行反汇编，看到一串汇编代码，非常精妙，最终发现仅仅是为了计算两个整数的较小值。可现在非常努力的回忆，就是想不起来是怎么做的。<br />&nbsp;&nbsp;&nbsp;&nbsp; 真的非常想再现那串算法，于是自己开始推敲。我来谈谈我推敲的过程。<br />&nbsp;&nbsp;&nbsp;&nbsp; 命题：给定整数x,y，计算较小值m。<br />&nbsp;&nbsp;&nbsp;&nbsp; 两个数的差异，在于他们的差，于是想到计算z = x - y，我想也许可以利用这个中间值，利用一些巧妙的位运算求出，可是貌似还是比较困难。于是我打算重新理一下思路：<br />可能出现的情况：(暂时忽略特殊情况 z = 0)<br />1. x &lt; y<br />&nbsp;&nbsp;&nbsp; z &lt; 0<br />&nbsp;&nbsp;&nbsp; 就是要找到一个函数f，满足f(y , z) = x<br />2. x &gt; y<br />&nbsp;&nbsp;&nbsp; z &gt; 0<br />&nbsp;&nbsp;&nbsp; 就需要这个f不仅满足1，而且满足此时f(y , z) = y<br /><br />&nbsp;&nbsp;&nbsp; 因为算法的目的是使用加减法、位运算这些基本运算，尽可能简单的计算。所以我选择了加法运算<br />&nbsp;&nbsp;&nbsp; y + g(z) = x , z = x - y &lt; 0;<br />&nbsp;&nbsp;&nbsp; y + g(z) = y , z = x - y &gt; 0;<br />&nbsp;&nbsp;&nbsp; 最终变成寻求一元函数g<br />&nbsp;&nbsp;&nbsp; 就是<br />&nbsp;&nbsp;&nbsp; g(z) = z, z &lt; 0<br />&nbsp;&nbsp;&nbsp; g(z) = 0, z &gt; 0<br />&nbsp;&nbsp;&nbsp; 也就是要找到一个一元分段函数，而且需要运算简单，于是我想到了g(z) = (z &gt;&gt; 31) &amp; z<br />&nbsp;&nbsp;&nbsp; 如果z &lt; 0，z&gt;&gt;31得到的是FFFFFFFF，再与上一个z，还是z，<br />&nbsp;&nbsp;&nbsp; 如果z &gt; 0,&nbsp; z&gt;&gt;31得到的是0000000，最终还是0<br />&nbsp;&nbsp;&nbsp; 所以最终的算法是<br />&nbsp;&nbsp;&nbsp; z = x - y<br />&nbsp;&nbsp;&nbsp; m = ((z &gt;&gt; 31) &amp; z) + y;<br />&nbsp;&nbsp;&nbsp; 这个算法应该跟当初看到的比较接近了。它的优点很显然，全部是最基本的运算，而且不包含控制指令，而且完全可以直接由寄存器计算完成，效率很高。<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; 算法本身并非什么惊天地泣鬼神大算法，而且在编译器里肯定会有自己做这样的优化，其实最让我欣慰的是我这次的思路，思路非常清晰，很久没有动脑子的我，居然还能这么思考，我已经很高兴了。其中主要包含两种思想：分类讨论、降低元数(降二元为一元)。这也是使用非常广泛的方法了，前者主要帮助理清思路，后者主要降低复杂度。<br /><br /><strong>Updated:</strong><br />&nbsp;&nbsp;&nbsp; 之前用的是z&gt;&gt;32，用gcc编译会出现一个警告：<div>&nbsp;&nbsp;&nbsp; right shift count &gt;= width of type [enabled by default]</div>&nbsp;&nbsp;&nbsp; 但还不清楚会存在什么样的隐患，所以改成31<img src ="http://www.cppblog.com/yefeng/aggbug/154091.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yefeng/" target="_blank">夜风</a> 2011-08-22 23:58 <a href="http://www.cppblog.com/yefeng/archive/2011/08/22/154091.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Aho-Corasick算法实践</title><link>http://www.cppblog.com/yefeng/archive/2009/12/06/102671.html</link><dc:creator>夜风</dc:creator><author>夜风</author><pubDate>Sun, 06 Dec 2009 14:51:00 GMT</pubDate><guid>http://www.cppblog.com/yefeng/archive/2009/12/06/102671.html</guid><wfw:comment>http://www.cppblog.com/yefeng/comments/102671.html</wfw:comment><comments>http://www.cppblog.com/yefeng/archive/2009/12/06/102671.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yefeng/comments/commentRss/102671.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yefeng/services/trackbacks/102671.html</trackback:ping><description><![CDATA[<meta http-equiv="Content-Type" content="text/html; charset=" utf-8="">
<meta name="ProgId" content="Word.Document">
<meta name="Generator" content="Microsoft Word 11">
<meta name="Originator" content="Microsoft Word 11">
<link rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyefeng%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">
<link rel="Edit-Time-Data" href="file:///C:%5CDOCUME%7E1%5Cyefeng%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_editdata.mso">
<link rel="OLE-Object-Data" href="file:///C:%5CDOCUME%7E1%5Cyefeng%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_oledata.mso"><!--[if !mso]>
<style>
v\:* {behavior:url(#default#VML);}
o\:* {behavior:url(#default#VML);}
w\:* {behavior:url(#default#VML);}
.shape {behavior:url(#default#VML);}
</style>
<![endif]--><!--[if gte mso 9]><xml>
Normal
0
7.8 磅
0
2
false
false
false
MicrosoftInternetExplorer4
</xml><![endif]--><!--[if gte mso 9]><xml>
</xml><![endif]--><style>
<!--
/* Font Definitions */
@font-face
{font-family:宋体;
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-alt:SimSun;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:3 135135232 16 0 262145 0;}
@font-face
{font-family:黑体;
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-alt:SimHei;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:1 135135232 16 0 262144 0;}
@font-face
{font-family:"\@宋体";
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:3 135135232 16 0 262145 0;}
@font-face
{font-family:"\@黑体";
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:1 135135232 16 0 262144 0;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{mso-style-parent:"";
margin:0cm;
margin-bottom:.0001pt;
text-align:justify;
text-justify:inter-ideograph;
mso-pagination:none;
font-size:10.5pt;
mso-bidi-font-size:12.0pt;
font-family:"Times New Roman";
mso-fareast-font-family:宋体;
mso-font-kerning:1.0pt;}
h1
{mso-style-next:正文;
margin-top:17.0pt;
margin-right:0cm;
margin-bottom:16.5pt;
margin-left:0cm;
text-align:center;
line-height:240%;
mso-pagination:lines-together;
page-break-after:avoid;
mso-outline-level:1;
font-size:22.0pt;
font-family:"Times New Roman";
mso-font-kerning:22.0pt;}
h2
{mso-style-next:正文;
margin-top:13.0pt;
margin-right:0cm;
margin-bottom:13.0pt;
margin-left:21.0pt;
text-align:justify;
text-justify:inter-ideograph;
text-indent:-21.0pt;
line-height:173%;
mso-pagination:lines-together;
page-break-after:avoid;
mso-outline-level:2;
mso-list:l1 level1 lfo1;
tab-stops:list 21.0pt;
font-size:16.0pt;
font-family:Arial;
mso-fareast-font-family:黑体;
mso-bidi-font-family:"Times New Roman";
mso-font-kerning:1.0pt;}
h3
{mso-style-next:正文;
margin-top:13.0pt;
margin-right:0cm;
margin-bottom:13.0pt;
margin-left:21.0pt;
text-align:justify;
text-justify:inter-ideograph;
text-indent:-21.0pt;
line-height:173%;
mso-pagination:lines-together;
page-break-after:avoid;
mso-outline-level:3;
mso-list:l2 level1 lfo2;
tab-stops:list 21.0pt;
font-size:16.0pt;
font-family:"Times New Roman";
mso-font-kerning:1.0pt;}
/* Page Definitions */
@page
{mso-page-border-surround-header:no;
mso-page-border-surround-footer:no;}
@page Section1
{size:595.3pt 841.9pt;
margin:72.0pt 90.0pt 72.0pt 90.0pt;
mso-header-margin:42.55pt;
mso-footer-margin:49.6pt;
mso-paper-source:0;
layout-grid:15.6pt;}
div.Section1
{page:Section1;}
/* List Definitions */
@list l0
{mso-list-id:59057800;
mso-list-type:hybrid;
mso-list-template-ids:954916874 -1935886898 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;}
@list l0:level1
{mso-level-tab-stop:60.0pt;
mso-level-number-position:left;
margin-left:60.0pt;
text-indent:-18.0pt;}
@list l1
{mso-list-id:1262569428;
mso-list-type:hybrid;
mso-list-template-ids:-1597851618 277625704 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;}
@list l1:level1
{mso-level-number-format:chinese-counting-thousand;
mso-level-style-link:"标题 2";
mso-level-text:%1、;
mso-level-tab-stop:21.0pt;
mso-level-number-position:left;
margin-left:21.0pt;
text-indent:-21.0pt;}
@list l2
{mso-list-id:1873759819;
mso-list-type:hybrid;
mso-list-template-ids:-1017220182 1075860886 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;}
@list l2:level1
{mso-level-style-link:"标题 3";
mso-level-tab-stop:21.0pt;
mso-level-number-position:left;
margin-left:21.0pt;
text-indent:-21.0pt;}
ol
{margin-bottom:0cm;}
ul
{margin-bottom:0cm;}
-->
</style><!--[if gte mso 10]>
<style>
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Times New Roman";
mso-ansi-language:#0400;
mso-fareast-language:#0400;
mso-bidi-language:#0400;}
table.MsoTableGrid
{mso-style-name:网格型;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
border:solid windowtext 1.0pt;
mso-border-alt:solid windowtext .5pt;
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-border-insideh:.5pt solid windowtext;
mso-border-insidev:.5pt solid windowtext;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
text-align:justify;
text-justify:inter-ideograph;
mso-pagination:none;
font-size:10.0pt;
font-family:"Times New Roman";
mso-ansi-language:#0400;
mso-fareast-language:#0400;
mso-bidi-language:#0400;}
</style>
<![endif]-->
<h1>Aho-Corasick算法实践</h1>
<strong><span style="font-size: 10pt;">摘要：</span></strong><span style="font-size: 12pt;"><span style="font-size: 10pt;">
<p>&nbsp;&nbsp;&nbsp; Aho-Corasick算法可以在文本串中识别一组关键字，所需时间和文本长度以及所有关键字的总长度成正比。该算法使用了一种称为&#8220;trie&#8221;的特殊形式的状态装换图。Trie是一个树形结构的状态装换图，从一个结点到它的各个子结点的边上有不同的标号。Trie的叶子结点表示识别到的关键字。</p>
<p>&nbsp;&nbsp;&nbsp; 在这里，将着重讨论算法的实现。算法包含两个部分，一是经典的KMP算法，二是KMP的扩展算法Aho-Corasick算法。前者实现单关键字的模式匹配，后者实现多关键字的匹配。(参考龙书词法分析部分内容)</p>
<p>&nbsp;&nbsp; 【源代码：<a style="color: #303dff;" href="http://www.cppblog.com/Files/yefeng/ACKMP.rar">http://www.cppblog.com/Files/yefeng/ACKMP.rar</a>(vc9.0下测试通过)
】</p>
</span></span>
<meta http-equiv="Content-Type" content="text/html; charset=" utf-8="">
<meta name="ProgId" content="Word.Document">
<meta name="Generator" content="Microsoft Word 11">
<meta name="Originator" content="Microsoft Word 11">
<link rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyefeng%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml"><!--[if gte mso 9]><xml>
Normal
0
7.8 磅
0
2
false
false
false
MicrosoftInternetExplorer4
</xml><![endif]--><!--[if gte mso 9]><xml>
</xml><![endif]--><style>
<!--
/* Font Definitions */
@font-face
{font-family:宋体;
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-alt:SimSun;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:3 135135232 16 0 262145 0;}
@font-face
{font-family:黑体;
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-alt:SimHei;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:1 135135232 16 0 262144 0;}
@font-face
{font-family:"\@宋体";
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:3 135135232 16 0 262145 0;}
@font-face
{font-family:"\@黑体";
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:1 135135232 16 0 262144 0;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{mso-style-parent:"";
margin:0cm;
margin-bottom:.0001pt;
text-align:justify;
text-justify:inter-ideograph;
mso-pagination:none;
font-size:10.5pt;
mso-bidi-font-size:12.0pt;
font-family:"Times New Roman";
mso-fareast-font-family:宋体;
mso-font-kerning:1.0pt;}
h2
{mso-style-next:正文;
margin-top:13.0pt;
margin-right:0cm;
margin-bottom:13.0pt;
margin-left:21.0pt;
text-align:justify;
text-justify:inter-ideograph;
text-indent:-21.0pt;
line-height:173%;
mso-pagination:lines-together;
page-break-after:avoid;
mso-outline-level:2;
mso-list:l0 level1 lfo1;
tab-stops:list 21.0pt;
font-size:16.0pt;
font-family:Arial;
mso-fareast-font-family:黑体;
mso-bidi-font-family:"Times New Roman";
mso-font-kerning:1.0pt;}
/* Page Definitions */
@page
{mso-page-border-surround-header:no;
mso-page-border-surround-footer:no;}
@page Section1
{size:612.0pt 792.0pt;
margin:72.0pt 90.0pt 72.0pt 90.0pt;
mso-header-margin:36.0pt;
mso-footer-margin:36.0pt;
mso-paper-source:0;}
div.Section1
{page:Section1;}
/* List Definitions */
@list l0
{mso-list-id:1262569428;
mso-list-type:hybrid;
mso-list-template-ids:-1597851618 277625704 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;}
@list l0:level1
{mso-level-number-format:chinese-counting-thousand;
mso-level-style-link:"标题 2";
mso-level-text:%1、;
mso-level-tab-stop:21.0pt;
mso-level-number-position:left;
margin-left:21.0pt;
text-indent:-21.0pt;}
ol
{margin-bottom:0cm;}
ul
{margin-bottom:0cm;}
-->
</style><!--[if gte mso 10]>
<style>
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Times New Roman";
mso-ansi-language:#0400;
mso-fareast-language:#0400;
mso-bidi-language:#0400;}
</style>
<![endif]-->
<h2><!--[if !supportLists]-->一、<!--[endif]-->经典KMP算法</h2>
&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">当初，初学KMP算法时，总是通过反复的举例去理解，没有一种好的表达方式，而龙书描述这个算法使用了trie树，也就是一个单链的状态转换图。如模式<!--[if gte vml 1]>
<![endif]--><!--[if !vml]-->b0b1...bn-1<!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]-->，trie树如下：</span>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <img style="width: 511px; height: 99px;" alt="" src="http://www.cppblog.com/images/cppblog_com/yefeng/clip_image002.jpg"><br><!--[endif]--></p>
<span style="font-size: 10pt;">
<p>&nbsp;&nbsp;&nbsp; 对模式串定义失效函数<!--[if gte vml 1]>
<![endif]--><!--[if !vml]-->f:x-&gt;y,x,y in S<!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]-->，描述状态转移，<!--[if gte vml 1]>
<![endif]--><!--[if !vml]--><!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]-->f(s)表示在状态s处，当下一个字符不是bs<!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]-->时转向状态<!--[if gte vml 1]>
<![endif]--><!--[if !vml]-->f(s)<!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]-->继续匹配。因此设置<!--[if gte vml 1]>
<![endif]--><!--[if !vml]-->f(s)<!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]-->成为关键问题。</p>
<p><!--[if gte vml 1]>
<![endif]--><!--[if !vml]--><!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]-->&nbsp;&nbsp;&nbsp; f(s)的存在其实主要是为了消除回溯。细节就不再多说了，这里只从原理上简单说明。</p>
<p>&nbsp;&nbsp;&nbsp; 设模式串为W，用文法描述，U、V表示W的一部分,w表示一个字符：</p>
<p style="text-align: center;">&nbsp;&nbsp;&nbsp; W -&gt; UwV，</p>
<p>&nbsp;&nbsp;&nbsp; 当U识别完成后，进入状态s，识别w时，发现到来的字符不等于w，则需要转向状态<!--[if gte vml 1]>
<![endif]--><!--[if !vml]-->f(s)<!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]-->，f(s)<!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]-->到哪里去找呢？</p>
<p style="text-align: left;">&nbsp;&nbsp;&nbsp; 那就要看U是什么样子了。不管什么情况，只要U非空串，总可以表示成：</p>
<p style="text-align: center;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; U -&gt; uXu，或 U -&gt; u，或U-&gt; uXx，(x != u)</p>
<p>&nbsp;&nbsp;&nbsp; 可以发现，前缀u是，如果后缀也是u，意味着主串中u已经被识别，如果还从模式串头匹配u无疑是多余的，所以<!--[if gte vml 1]>
<![endif]--><!--[if !vml]-->f(s)<!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]-->应该是识别前缀u后进入的状态。然后再匹配下一个字符。而满足条件的u可能会有多个，所以总是选择最长的那个。伪代码如下：</p>
</span>
<p><!--[if gte vml 1]>
<![endif]--><!--[if !vml]-->&nbsp;&nbsp;&nbsp; <img alt="" src="http://www.cppblog.com/images/cppblog_com/yefeng/clip_image002.gif" height="167" width="225"><br><!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]--></p>
<span style="font-size: 10pt;">
<p>&nbsp;&nbsp;&nbsp; 到此为止，应该算是可以结束KMP了，但实际情况下还可以对f函数进行优化。很多书本上描述的next数组就可以从f函数推导过来。</p>
<p>&nbsp;&nbsp;&nbsp; 其实也显然，设状态s接收字符w，当与输入字符c不等于c时，转向状态t，倘若t状态也只接收字符w，显然再次比较w与c是多余的，之后必然再次转向状态f(t)。在运行的时候，这些状态转换时没有意义的，可以在构造f之后，直接将f(s)设置为f(t)提高运行效率(不过此时f函数的意义已经不同了)。f优化如下：</p>
</span>
<p><!--[if gte vml 1]>
<![endif]--><!--[if !vml]-->&nbsp;&nbsp;&nbsp; <img alt="" src="http://www.cppblog.com/images/cppblog_com/yefeng/a.gif" height="167" width="225"><br><!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]--></p>
<h2><!--[if !supportLists]-->二、<!--[endif]-->多关键字匹配与Aho-Corasick算法</h2>
<p>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">Aho和Corasick对KMP算法进行了推广，使它可以在一个文本串识别一个关键字集合中的任何关键字。在这种情况下，trie是一棵真正的树，从其根结点开始就会出现分支。如果一个字符串是某个关键字的前缀，那么在trie中就又一个和该字符串对应的状态。如关键字集合{he,she,his,hers}，trie树如下：</span></p>
<p><!--[if gte vml 1]>
<![endif]--><!--[if !vml]-->&nbsp;&nbsp;&nbsp; <img style="width: 444px; height: 248px;" src="http://www.cppblog.com/images/cppblog_com/yefeng/bb.jpg" border="0"><br><!--[endif]--></p>
<p>&nbsp;&nbsp;&nbsp; </p>
<span style="font-size: 10pt;">
<p>&nbsp;&nbsp;&nbsp; 类似的，仍然构造类似KMP算法中那样的实效函数。对于上面的例子，失效函数如下：</p>
<table class="MsoTableGrid" style="margin-left: 21pt; border-collapse: collapse;" border="1" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 38.7pt;" valign="top" width="52">
            <p>s</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.7pt;" valign="top" width="52">
            <p>0</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.7pt;" valign="top" width="52">
            <p>1</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>2</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>3</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>4</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>5</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>6</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>7</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>8</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>9</p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 38.7pt;" valign="top" width="52">
            <p>f(s)</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.7pt;" valign="top" width="52">
            <p>-1</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.7pt;" valign="top" width="52">
            <p>0</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>0</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>0</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>1</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>2</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>0</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>3</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>0</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 38.75pt;" valign="top" width="52">
            <p>3</p>
            </td>
        </tr>
    </tbody>
</table>
</span><br>
<h3><!--[if !supportLists]-->&nbsp; 1.<!--[endif]-->构造失效函数</h3>
<p>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">类似KMP算法，同样采用实效实效函数推进的方法，假设当前状态为s，s的一个孩子结点的根结点根节点t状态，如果当前的失效函数已知为f(s)，则显然地，f(t)必定是f(s)的孩子结点状态，所要做的就是在状态f(s)处寻找接受字符同s-&gt;t下一个状态，如果能找到，那就是f(t)，否则说明到s处匹配串的前缀长度太长，需缩减，所以需要找到更短的后缀，于是就到f(s)处继续，如果仍然找不到，则转到f(f(s))处，形成状态的递归转移。构造中需要遍历之前结点的所有孩子，所以需采用广度优先遍历，伪代码如下：</span></p>
<p><!--[if gte vml 1]>
<![endif]--><!--[if !vml]-->&nbsp;&nbsp;&nbsp; <img alt="" src="http://www.cppblog.com/images/cppblog_com/yefeng/cc.gif" height="295" width="180"><br><!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]--></p>
<p>&nbsp;&nbsp;&nbsp; <span style="font-size: 10pt;">具体的构造如下：</span></p>
<p><!--[if gte vml 1]>
<![endif]--><!--[if !vml]-->&nbsp;&nbsp;&nbsp; <img style="width: 194px; height: 289px;" alt="" src="http://www.cppblog.com/images/cppblog_com/yefeng/dd.gif"><br><!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]--></p>
<h3><!--[if !supportLists]-->&nbsp; 2.构造Trie树</h3>
<span style="font-size: 10pt;">
<p>&nbsp;&nbsp;&nbsp; 具体实现当然需要用到树形结构了，显然采用静态链表应该是最适合的，因为树构造完就不需要改变，而且当模式串比较多的时候可以减少内存碎片。</p>
<p>&nbsp; &nbsp; 每一个结点有5个域：接受字符，下一个兄弟结点，第一个孩子结点，失效函数值，结点状态。</p>
<p>但是有一种特殊情况，如上面的第二个图，在进行匹配时，hers是永远不会被匹配，因为he总是先于hers被匹配。这里就不考虑在内点状态结束，这个问题暂时无法解决。于是可以做个特殊处理，只使用4个域，因为此时匹配成功后状态就到了叶子结点，叶子结点不存在孩子域，这个域被浪费了，这里就可以借用一下，比如此域值为x，当x&lt;0时，使用x xor 0x80000000表示识别到的模式串编号。</p>
<p>&nbsp;&nbsp;&nbsp; 另一个棘手的问题是结点个数问题，这个数组到底多大？如何确定？</p>
<p>&nbsp;&nbsp;&nbsp; 可以使用分值算法计算，先把模式串按字典顺序排好序，设想n个排好序的模式串第i位排在一起，相同字符的组成一组，如AiBi&#8230;Xi，再把每组下一个字符，也就是第i+1位排在一起，相同字符的组成一组，如A&#8217;iB&#8217;I&#8230;X&#8217;i，以此递归运算。伪代码如下：</p>
</span>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <img style="width: 334px; height: 288px;" alt="" src="http://www.cppblog.com/images/cppblog_com/yefeng/xx.gif"><br><!--[endif]--><!--[if gte mso 9]><xml>
</xml><![endif]--></p>
<h3><!--[if !supportLists]-->&nbsp; 3.缺点</h3>
<span style="font-size: 10pt;">
<p>&nbsp;&nbsp;&nbsp; 水平有限，程序缺点很多，很多问题都没有解决。</p>
<p><!--[if !supportLists]-->&nbsp;&nbsp;&nbsp; 1.<!--[endif]-->如果存在两个模式串，一个是另一个的子串，那么后者将无法被匹配。</p>
<p><!--[if !supportLists]-->&nbsp;&nbsp;&nbsp; 2.<!--[endif]-->无法处理动态决定大小写敏感性</p>
<p><!--[if !supportLists]-->&nbsp;&nbsp;&nbsp; 3.<!--[endif]-->不够完整，只能向后匹配</p>
</span> <img src ="http://www.cppblog.com/yefeng/aggbug/102671.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yefeng/" target="_blank">夜风</a> 2009-12-06 22:51 <a href="http://www.cppblog.com/yefeng/archive/2009/12/06/102671.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>