﻿<?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++博客-c++初学者-随笔分类-图像算法</title><link>http://www.cppblog.com/tgh621/category/8151.html</link><description>专注技术开发</description><language>zh-cn</language><lastBuildDate>Wed, 12 Nov 2008 02:42:37 GMT</lastBuildDate><pubDate>Wed, 12 Nov 2008 02:42:37 GMT</pubDate><ttl>60</ttl><item><title> 【转】字符串匹配算法（四）可以滑动多远</title><link>http://www.cppblog.com/tgh621/archive/2008/11/11/66596.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Tue, 11 Nov 2008 05:09:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2008/11/11/66596.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/66596.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2008/11/11/66596.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/66596.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/66596.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 记得在穷举法中，每一趟比较后，无论成与不成，都将模式向右滑动一个位置，然后继续比较。有没有办法能利用之前的比较结果，使得模式滑动的更远一点呢？在介绍经典的KMP算法前，我先介绍几个简单的滑动类算法。Not So Naive同名字一样，这个算法的确有点幼稚，它根据模式的前两个字符是否相同来滑动比穷举法稍长一点的距离：如果前两个字符相同，那么文本中与第二个字符不同则必然也与第一...&nbsp;&nbsp;<a href='http://www.cppblog.com/tgh621/archive/2008/11/11/66596.html'>阅读全文</a><img src ="http://www.cppblog.com/tgh621/aggbug/66596.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2008-11-11 13:09 <a href="http://www.cppblog.com/tgh621/archive/2008/11/11/66596.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 【转】字符串匹配算法（三）位运算的魔法——KR与SO</title><link>http://www.cppblog.com/tgh621/archive/2008/11/11/66595.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Tue, 11 Nov 2008 05:08:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2008/11/11/66595.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/66595.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2008/11/11/66595.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/66595.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/66595.html</trackback:ping><description><![CDATA[<div>位运算经常能做出一些不可思议的事情来，例如不用临时变量要交换两个数该怎么做呢？一个没接触过这类问题的人打死他也想不出来。如果拿围棋来做比喻，那么位运算可以喻为编程中的&#8220;手筋&#8221;。</div>
<div><br></div>
<div>按位的存储方式能提供最大的存储空间利用率，而随着空间被压缩的同时，由于CPU硬件的直接支持，速度竟然神奇般的提升了。举个例子，普通的数组要实现移位操作，那是O(n)的时间复杂度，而如果用位运算中的移位，就是一个指令搞定了。</div>
<div><br></div>
<div><span class=Apple-style-span style="FONT-WEIGHT: bold">KR算法</span></div>
<div><span class=Apple-style-span style="FONT-WEIGHT: bold"><br></span></div>
<div>KR算法之前第一章介绍中说是利用哈希，原文这么介绍的。而我的看法是，哈希只是一个幌子。这个算法的基本步骤同穷举法一样，不同在于每趟比较前先比较一下哈希值，hash值不同就不必比较了。而如果hash值无法高效计算，这样的改进甚至还不如不改进呢。你想想，比较之前还要先计算一遍hash值，有计算的功夫，直接比都比完了。</div>
<div><br></div>
<div>KR算法为了把挨个字符的比较转化为两个整数的比较，它把一个m长度的字符串直接当成一个整数来对待，以2为基数的整数。这样呢，在第一次算出这个整数后，以后每次移动窗口，只需要移去最高位，再加上最低位，就得出一个新的hash值。但是m太大，导致超出计算机所能处理的最大整数怎么办？不用担心，对整数最大值取模，借助模运算的特性，一切可以完美的进行。而且由于是对整数最大值取模，所以取模这一步都可以忽略掉。</div>
<div><br></div>
<div>这是KR算法的代码：</div>
<div>
<div class=highlighter>
<ol class=highlighter-cpp>
    <li><font color=#808080><span class=preprocessor>#define&nbsp;REHASH(a,&nbsp;b,&nbsp;h)&nbsp;((((h)&nbsp;-&nbsp;(a)*d)&nbsp;&lt;&lt;&nbsp;1)&nbsp;+&nbsp;(b))</span><span></span></font>
    <li class=alt><span></span>
    <li><span></span><span class=keyword><strong><font color=#0000ff>void</font></strong></span><span>&nbsp;KR(</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*x,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;m,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*y,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;n)&nbsp;{</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;d,&nbsp;hx,&nbsp;hy,&nbsp;i,&nbsp;j;</span>
    <li><span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;</span><font color=#008200><span class=comment>/*&nbsp;Preprocessing&nbsp;*/</span><span></span></font>
    <li><span>&nbsp;&nbsp;&nbsp;</span><span class=comment><font color=#008200>/*&nbsp;computes&nbsp;d&nbsp;=&nbsp;2^(m-1)&nbsp;with</font></span>
    <li class=alt><font color=#008200><span class=comment>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;left-shift&nbsp;operator&nbsp;*/</span><span></span></font>
    <li>&nbsp;&nbsp;&nbsp;<span class=keyword><strong><font color=#0000ff>for</font></strong></span><span>&nbsp;(d&nbsp;=&nbsp;i&nbsp;=&nbsp;1;&nbsp;i&nbsp;&lt;&nbsp;m;&nbsp;++i)</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;d&nbsp;=&nbsp;(d&lt;&lt;1);</span>
    <li><span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>for</font></strong></span><span>&nbsp;(hy&nbsp;=&nbsp;hx&nbsp;=&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;m;&nbsp;++i)&nbsp;{</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hx&nbsp;=&nbsp;((hx&lt;&lt;1)&nbsp;+&nbsp;x[i]);</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hy&nbsp;=&nbsp;((hy&lt;&lt;1)&nbsp;+&nbsp;y[i]);</span>
    <li><span>&nbsp;&nbsp;&nbsp;}</span>
    <li class=alt><span></span>
    <li><span>&nbsp;&nbsp;&nbsp;</span><font color=#008200><span class=comment>/*&nbsp;Searching&nbsp;*/</span><span></span></font>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;j&nbsp;=&nbsp;0;</span>
    <li><span>&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>while</font></strong></span><span>&nbsp;(j&nbsp;&lt;=&nbsp;n-m)&nbsp;{</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>if</font></strong></span><span>&nbsp;(hx&nbsp;==&nbsp;hy&nbsp;&amp;&amp;&nbsp;memcmp(x,&nbsp;y&nbsp;+&nbsp;j,&nbsp;m)&nbsp;==&nbsp;0)</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OUTPUT(j);</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hy&nbsp;=&nbsp;REHASH(y[j],&nbsp;y[j&nbsp;+&nbsp;m],&nbsp;hy);</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;++j;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;}</span>
    <li><span></span>
    <li class=alt><span>}</span>
    <li><span></span>
    <li class=alt><span></span></li>
</ol>
</div>
我们可以看到，KR算法有O(m)复杂度的预处理的过程，总感觉它的预处理没有反映出模式本身的特点来，导致它的搜索过程依然是O(mn)复杂度的，只不过一般情况下体现不出来，在"aaaaaaaaaaaaaaaaaaaaaaaaa"中搜"aaaaa"就知道KR多慢了。</div>
<div><br></div>
<div>总的来说，KR算法比穷举强一点，比较次数的期望值是O(m+n)。</div>
<div><br></div>
<div><span class=Apple-style-span style="FONT-WEIGHT: bold">Shift Or 算法</span></div>
<div><span class=Apple-style-span style="FONT-WEIGHT: bold"><br></span></div>
<div>为了最大限度的发挥出位运算的能力，Shift Or算法就有了一个最大缺陷：模式不能超过机器字长。按现在普遍的32位机，机器字长就是32，也就是只能用来匹配不大于32个字符的模式。而带来的好处就是匹配过程是O(n)时间复杂度的，达到自动机的速度了。而预处理所花费的时间与空间都为O(m+&#963;)，比自动机少多了。</div>
<div><br></div>
<div>我们来看看它怎么巧妙的实现&#8220;只看一遍&#8221;的：</div>
<div><br></div>
<div>假设我们有一个升级系统，总共有m个级别。每一关都会放一个新人到第0级上，然后对于系统中所有的人，如果通过考验，升一级，否则，咔嚓掉。而对于升到最高级的人，那说明他连续通过了m次考验，这就是我们要选拔的人。</div>
<div><br></div>
<div>KR算法的思路就是上面的升级规则，给出的考验就是你的位置上的字符与给出的文本字符是否一致。升满级了，说明在连续m个位置上与不断给出的文本字符一致，这也就是匹配成功了。</div>
<div><br></div>
<div>明白了这个思路后，疑问就开始出来了：检查哪些位置与文本字符一致，需要m次吧？那么整个算法就是O(mn)了？</div>
<div><br></div>
<div>现在就该位运算出场了，对，这个算法的思路是很笨，但是我位运算的效率高呀，事先我算出字母表中每个字符在模式中出现的位置，用位的方式存在整数里，出现的地方标为0，不出现的地方标为1，这样总共使用&#963;个整数；同样，我用一个整数来表示升级状态，某个级别有人就标为0，没人就标为1，整个系统升级就恰好可以用&#8220;移位&#8221;来进行，当检查位置的时候只需要与表示状态的整数&#8220;或&#8221;1次，所以整个算法就成O(n)了。Shift-Or算法名字就是这样来的。</div>
<div><br></div>
<div>有一个地方很奇怪，0和1的设定和通常的习惯相反呀，习惯上，喜欢把存在设为1，不存在设为0的。不过这里没有办法，因为移位新移出来的是0。</div>
<div><br></div>
<div>这时我们来看代码就容易理解多了：</div>
<div>
<div class=highlighter>
<ol class=highlighter-cpp>
    <li><font color=#808080><span class=preprocessor>#define&nbsp;WORDSIZE&nbsp;sizeof(int)*8</span><span></span></font>
    <li><span class=Apple-style-span style="COLOR: rgb(128,128,128)">#define ASIZE 256</span>
    <li class=alt><span></span>
    <li><span></span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;preSo(</span><span class=keyword><strong><font color=#0000ff>const</font></strong></span><span>&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*x,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;m,&nbsp;unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;S[])&nbsp;{</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;j,&nbsp;lim;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;i;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>for</font></strong></span><span>&nbsp;(i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;ASIZE;&nbsp;++i)</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;S[i]&nbsp;=&nbsp;~0;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>for</font></strong></span><span>&nbsp;(lim&nbsp;=&nbsp;i&nbsp;=&nbsp;0,&nbsp;j&nbsp;=&nbsp;1;&nbsp;i&nbsp;&lt;&nbsp;m;&nbsp;++i,&nbsp;j&nbsp;&lt;&lt;=&nbsp;1)&nbsp;{</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;S[x[i]]&nbsp;&amp;=&nbsp;~j;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lim&nbsp;|=&nbsp;j;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lim&nbsp;=&nbsp;~(lim&gt;&gt;1);</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>return</font></strong></span><span>(lim);</span>
    <li class=alt><span>}</span>
    <li><span></span>
    <li class=alt><span></span><span class=keyword><strong><font color=#0000ff>void</font></strong></span><span>&nbsp;SO(</span><span class=keyword><strong><font color=#0000ff>const</font></strong></span><span>&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*x,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;m,&nbsp;</span><span class=keyword><strong><font color=#0000ff>const</font></strong></span><span>&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*y,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;n)&nbsp;{</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;lim,&nbsp;state;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;S[ASIZE];</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;j;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>if</font></strong></span><span>&nbsp;(m&nbsp;&gt;&nbsp;WORDSIZE)</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;error(</span><span class=string><font color=#a31515>"SO:&nbsp;Use&nbsp;pattern&nbsp;size&nbsp;&lt;=&nbsp;word&nbsp;size"</font></span><span>);</span>
    <li class=alt><span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><font color=#008200><span class=comment>/*&nbsp;Preprocessing&nbsp;*/</span><span></span></font>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lim&nbsp;=&nbsp;preSo(x,&nbsp;m,&nbsp;S);</span>
    <li><span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><font color=#008200><span class=comment>/*&nbsp;Searching&nbsp;*/</span><span></span></font>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>for</font></strong></span><span>&nbsp;(state&nbsp;=&nbsp;~0,&nbsp;j&nbsp;=&nbsp;0;&nbsp;j&nbsp;&lt;&nbsp;n;&nbsp;++j)&nbsp;{</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;state&nbsp;=&nbsp;(state&lt;&lt;1)&nbsp;|&nbsp;S[y[j]];</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>if</font></strong></span><span>&nbsp;(state&nbsp;&lt;&nbsp;lim)</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OUTPUT(j&nbsp;-&nbsp;m&nbsp;+&nbsp;1);</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span>
    <li class=alt><span>}</span></li>
</ol>
</div>
</div>
代码中lim变量其实就是一个标尺，例如出现最高级的状态是01111111，那么lim就成了10000000，因此只要小于lim，就表示最高级上的0出现了。
<div><br></div>
<div>原文中对Shift-Or算法的描述还是很难懂的，如果对着那段说明去看代码，有点不知所云的感觉。我还是直接对着代码才想出这个升级的比喻来。</div>
<img src ="http://www.cppblog.com/tgh621/aggbug/66595.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2008-11-11 13:08 <a href="http://www.cppblog.com/tgh621/archive/2008/11/11/66595.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> [转]字符串匹配算法（二）穷举与自动机</title><link>http://www.cppblog.com/tgh621/archive/2008/11/11/66594.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Tue, 11 Nov 2008 05:07:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2008/11/11/66594.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/66594.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2008/11/11/66594.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/66594.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/66594.html</trackback:ping><description><![CDATA[穷举法又叫暴力法。大多数程序员眼里，它是幼稚的，但大师们不这么认为。
<div><br>
<div><span class=Apple-style-span style="COLOR: rgb(0,0,0); LINE-HEIGHT: 18px; FONT-FAMILY: Arial">Rob Pike, 最伟大的C 语言大师之一, 在《Notes on C Programming》中阐述了一个原则：</span><span class=Apple-style-span style="COLOR: rgb(0,0,0); LINE-HEIGHT: 18px; FONT-FAMILY: Arial">花哨的算法比简单算法更容易出bug、更难实现，尽量使用简单的算法配合简单的数据结构。而</span><span class=Apple-style-span style="COLOR: rgb(0,0,0); LINE-HEIGHT: 18px; FONT-FAMILY: Arial">Ken Thompson——Unix 最初版本的设计者和实现者，禅宗偈语般地对Pike 的这一原则作了强调：&nbsp;<span class=Apple-style-span style="FONT-WEIGHT: bold">拿不准就穷举（When in doubt , use brute force）</span>。&nbsp;而对于装13爱好者来说，更是自豪的称其使用的是BF算法。</span></div>
<div><br></div>
<div>穷举法用在字符串匹配上，简单的描述就是，检查文本从0到n-m的每一个位置，看看从这个位置开始是否与模式匹配。这种方法还是有一些优点的，如：不需要预处理过程，需要的额外空间为常数，每一趟比较时可以以任意顺序进行。<br></div>
<div><br></div>
<div>尽管它的时间复杂度为O(mn)，例如在文本"aaaaaaaaaaaaaaaaaaaaaaaaaaa"中寻找"aaaaab"时，就完全体现出来了。但是算法的期望值却是2n，这表明该算法在实际应用中效率不低。</div>
<div><br></div>
<div>C代码如下：</div>
<div>
<div class=highlighter>
<ol class=highlighter-cpp>
    <li><span class=keyword><strong><font color=#0000ff>void</font></strong></span><span>&nbsp;BF(</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*x,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;m,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*y,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;n)&nbsp;{</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;i,&nbsp;j;</span>
    <li><span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;</span><font color=#008200><span class=comment>/*&nbsp;Searching&nbsp;*/</span><span></span></font>
    <li><span>&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>for</font></strong></span><span>&nbsp;(j&nbsp;=&nbsp;0;&nbsp;j&nbsp;&lt;=&nbsp;n&nbsp;-&nbsp;m;&nbsp;++j)&nbsp;{</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>for</font></strong></span><span>&nbsp;(i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;m&nbsp;&amp;&amp;&nbsp;x[i]&nbsp;==&nbsp;y[i&nbsp;+&nbsp;j];&nbsp;++i);</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>if</font></strong></span><span>&nbsp;(i&nbsp;&gt;=&nbsp;m)</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OUTPUT(j);</span>
    <li><span>&nbsp;&nbsp;&nbsp;}</span>
    <li class=alt><span>}</span>
    <li><span>&nbsp;&nbsp;</span></li>
</ol>
</div>
如果我们注意到C库函数是汇编优化过的，并通常能提供比C代码更高的性能的话，我们可以用memcmp来完成每一趟比较过程，从而达到更好的性能：</div>
<div>
<div class=highlighter>
<ol class=highlighter-cpp>
    <li><font color=#808080><span class=preprocessor>#define&nbsp;EOS&nbsp;'\0'</span><span></span></font>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;</span>
    <li><span></span><span class=keyword><strong><font color=#0000ff>void</font></strong></span><span>&nbsp;BF(</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*x,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;m,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*y,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;n)&nbsp;{&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*yb;&nbsp;</span>
    <li><span>&nbsp;&nbsp;</span><span class=comment><font color=#008200>/*&nbsp;Searching&nbsp;*/</font></span><span>&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>for</font></strong></span><span>&nbsp;(yb&nbsp;=&nbsp;y;&nbsp;*y&nbsp;!=&nbsp;EOS;&nbsp;++y)&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>if</font></strong></span><span>&nbsp;(memcmp(x,&nbsp;y,&nbsp;m)&nbsp;==&nbsp;0)&nbsp;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OUTPUT(y&nbsp;-&nbsp;yb);</span>
    <li><span>}</span>
    <li class=alt><span></span>
    <li><span></span></li>
</ol>
</div>
自动机的方法其实和穷举法有点相似，都是用最简单直白的方式来做事情。区别在于穷举法是在计算，而自动机则是查表。尽管自动机的构造过程有一点点难解，要涉及到DFA的理论，但是自动机的比较过程那绝对是简单到无语。</div>
<div><br></div>
<div>简单说来，根据模式串，画好了一张大的表格，表格m+1行&#963;列，这里&#963;表示字母表的大小。表格每一行表示一种状态，状态数比模式长度多1。一开始的状态是0，也就是处在表格的第0行，这一行的每个元素指示了当遇到某字符时就跳转到另一个状态。每当跳转到最终状态时，表示找到了一个匹配。</div>
<div><br></div>
<div>语言表述起来还是比较啰嗦，看代码就知道了：</div>
<div>
<div class=highlighter>
<ol class=highlighter-cpp>
    <li><font color=#808080><span class=preprocessor>#define&nbsp;ASIZE&nbsp;256</span><span></span></font>
    <li class=alt><span></span>
    <li><span></span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;preAut(</span><span class=keyword><strong><font color=#0000ff>const</font></strong></span><span>&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*x,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;m,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>*&nbsp;aut)&nbsp;{</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;i,&nbsp;state,&nbsp;target,&nbsp;old;</span>
    <li><span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>for</font></strong></span><span>&nbsp;(state&nbsp;=&nbsp;0,&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;m;&nbsp;++i)&nbsp;{</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;target&nbsp;=&nbsp;i&nbsp;+&nbsp;1;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;old&nbsp;=&nbsp;aut[state&nbsp;*&nbsp;ASIZE&nbsp;+&nbsp;x[i]];</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;aut[state&nbsp;*&nbsp;ASIZE&nbsp;+&nbsp;x[i]]&nbsp;=&nbsp;target;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memcpy(aut&nbsp;+&nbsp;target&nbsp;*&nbsp;ASIZE,&nbsp;aut&nbsp;+&nbsp;old&nbsp;*&nbsp;ASIZE,&nbsp;ASIZE*</span><span class=keyword><strong><font color=#0000ff>sizeof</font></strong></span><span>(</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>));</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;state&nbsp;=&nbsp;target;</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>return</font></strong></span><span>&nbsp;state;</span>
    <li class=alt><span>}</span>
    <li><span></span>
    <li class=alt><span></span>
    <li><span></span><span class=keyword><strong><font color=#0000ff>void</font></strong></span><span>&nbsp;AUT(</span><span class=keyword><strong><font color=#0000ff>const</font></strong></span><span>&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*x,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;m,&nbsp;</span><span class=keyword><strong><font color=#0000ff>const</font></strong></span><span>&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>char</font></strong></span><span>&nbsp;*y,&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;n)&nbsp;{</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;j,&nbsp;state;</span>
    <li><span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><font color=#008200><span class=comment>/*&nbsp;Preprocessing&nbsp;*/</span><span></span></font>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;*aut&nbsp;=&nbsp;(</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>*)calloc((m+1)*ASIZE,&nbsp;</span><span class=keyword><strong><font color=#0000ff>sizeof</font></strong></span><span>(</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>));</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=datatypes><strong><font color=#2e8b57>int</font></strong></span><span>&nbsp;Terminal&nbsp;=&nbsp;preAut(x,&nbsp;m,&nbsp;aut);</span>
    <li><span></span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><font color=#008200><span class=comment>/*&nbsp;Searching&nbsp;*/</span><span></span></font>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>for</font></strong></span><span>&nbsp;(state&nbsp;=&nbsp;0,&nbsp;j&nbsp;=&nbsp;0;&nbsp;j&nbsp;&lt;&nbsp;n;&nbsp;++j)&nbsp;{</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;state&nbsp;=&nbsp;aut[state*ASIZE+y[j]];</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class=keyword><strong><font color=#0000ff>if</font></strong></span><span>&nbsp;(state&nbsp;==&nbsp;Terminal)</span>
    <li class=alt><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OUTPUT(j&nbsp;-&nbsp;m&nbsp;+&nbsp;1);</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span>
    <li class=alt><span>}</span></li>
</ol>
</div>
<div>（<span class=Apple-style-span style="COLOR: rgb(51,51,153)">注：原文的代码使用一个有向图的数据结构，我遵循大师的指引，改用了更简单一点的数组</span>）<br></div>
<div><br></div>
从代码上我们很容易看出，自动机的构造需要时间是O(m&#963;)，空间也是O(m&#963;)（严格来说这份代码使用了O((m+1)&#963;)），但是一旦构造完毕，接下来匹配的时间则是O(n)。</div>
<div><br></div>
<div>匹配的过程前面已经说了，太简单了没什么好说的，这里就解释一下构造过程吧！</div>
<div><br></div>
<div>我们构造的目标是对应模式长度，构造出同样多的状态，用0表示初始状态，然后第一个字符用状态1表示，第二个用状态2表示，依次类推，直到最后一个字符，用m表示，也是最终状态。</div>
<div><br></div>
<div>一开始，数组全都置0，，这个时候的自动机遇到任何字符都转到初始状态。然后给它看模式的第一个字符，假设这是'a'吧，告诉它，状态0遇到'a'应该到一个新的状态——状态1，所以把第0行的第'a'列修改为1。而这个时候状态1还是空白的，怎么办呢？</div>
<div><br></div>
<div>这时候状态0就想呀，在我被告知遇到'a'要去状态1之前，我原本遇到'a'都要去状态0的，也就是修改之前第'a'列所指的那个状态，称为old状态吧；而现在我遇到'a'却要去一个新的状态，既然以前old状态能处理遇到'a'之后的事情，那么我让新的状态像old状态一样就好了。于是状态0把old状态拷贝到状态1。</div>
<div><br></div>
<div>现在轮到状态1了，给它看第二个字符，它也如法炮制，指向了状态2，又把old状态拷贝给了状态2。</div>
<div><br></div>
<div>于是，状态机就在这种代代传承的过程中构造完毕了。</div>
<div><br></div>
<div>虽然理论上自动机是最完美的匹配方式，但是由于预处理的消耗过大，实践中，主要还是用于正则表达式。</div>
<div><br></div>
<div>结语：穷举法与自动机各自走了两个极端，因此都没能达到综合性能的最佳，本文之后介绍的算法，可以看成是在穷举和自动机两者之间取舍权衡的结果。</div>
</div>
<img src ="http://www.cppblog.com/tgh621/aggbug/66594.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2008-11-11 13:07 <a href="http://www.cppblog.com/tgh621/archive/2008/11/11/66594.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]字符串匹配算法（一）简介</title><link>http://www.cppblog.com/tgh621/archive/2008/11/11/66593.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Tue, 11 Nov 2008 05:05:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2008/11/11/66593.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/66593.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2008/11/11/66593.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/66593.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/66593.html</trackback:ping><description><![CDATA[<div>文本信息可以说是迄今为止最主要的一种信息交换手段，而作为文本处理中的一个重要领域——字符串匹配，就是我们今天要说的话题。（<span class=Apple-style-span style="COLOR: rgb(51,51,153)">原文还特意提及文本数据数量每18个月翻一番，以此论证算法必须要是高效的。不过我注意到摩尔定律也是18个月翻番，这正说明数据的增长是紧紧跟随处理速度的，因此越是使用高效的算法，将来待处理的数据就会越多。这也提示屏幕前的各位，代码不要写得太快了&#8230;&#8230;</span>）</div>
<div><br></div>
<div>字符串匹配指的是从文本中找出给定字符串（称为模式）的一个或所有出现的位置。本文的算法一律输出全部的匹配位置。模式串在代码中用x[m]来表示，文本用y[n]来，而所有字符串都构造自一个有限集的字母表&#931;，其大小为&#963;。</div>
<div><br></div>
<div>根据先给出模式还是先给出文本，字符串匹配分为两类方法：</div>
<div>
<ul>
    <li><span class=Apple-style-span style="LINE-HEIGHT: normal">第一类方法基于自动机或者字符串的组合特点，其实现上，通常是对模式进行预处理；</span><br>
    <li><span class=Apple-style-span style="LINE-HEIGHT: normal">第二类方法对文本建立索引，这也是现在搜索引擎采用的方法。</span></li>
</ul>
本文仅讨论第一类方法。<br></div>
<div><br></div>
<div>文中的匹配算法都是基于这样一种方式来进行的：设想一个长度为m的窗口，首先窗口的左端和文本的左端对齐，把窗口中的字符与模式字符进行比较，这称为一趟比较，当这一趟比较完全匹配或者出现失配时，将窗口向右移动。重复这个过程，直到窗口的右端到达了文本的右端。这种方法我们通常叫sliding window。</div>
<div><br></div>
<div>对于穷举法来说，找到所有匹配位置需要的时间为O(mn)，基于对穷举法改进的结果，我们按照每一趟比较时的比较顺序，把这些算法分为以下四种：</div>
<div>
<ol>
    <li>从左到右：最自然的方式，也是我们的阅读顺序
    <li>从右到左：通常在实践中能产生最好的算法
    <li>特殊顺序：可以达到理论上的极限
    <li>任意顺序：这些算法跟比较顺序没关系（例如：穷举法）</li>
</ol>
</div>
<div>一些主要算法的简单介绍如下：</div>
<div><br></div>
<div><span class=Apple-style-span style="FONT-WEIGHT: bold">从左到右</span></div>
<div>采用哈希，可以很容易在大部分情况下避免二次比较，通过合理的假设，这种算法是线性时间复杂度的。它最先由Harrison提出，而后由Karp和Rabin全面分析，称为KR算法。</div>
<div>在假设模式长度不大于机器字长时，Shift-Or算法是很高效的匹配算法，同时它可以很容易扩展到模糊匹配上。</div>
<div>MP是第一个线性时间算法，随后被改进为KMP，它的匹配方式很类似于自动机的识别过程，文本的每个字符与模式的每个字符比较不会超过log<span class=Apple-style-span style="VERTICAL-ALIGN: sub">&#934;</span>(m+1)，这里&#934;是黄金分隔比1.618，而随后发现的类似算法——Simon算法，使得文本的每个字符比较不超过1+log<span class=Apple-style-span style="VERTICAL-ALIGN: sub">2</span>m，这三种算法在最坏情况下都只要2n-1次比较。（<span class=Apple-style-span style="COLOR: rgb(51,51,153)">抱歉限于我的水平这一段既没看懂也没能查证，大家就看个意思吧</span>）</div>
<div>基于确定性有限自动机的算法对文本字符刚好只用n次访问，但是它需要额外的<span class=Apple-style-span style="FONT-FAMILY: 'Times New Roman'">O(m&#963;)</span>的空间。</div>
<div>一种叫Forward Dawg Matching的算法同样也只用n次访问，它使用了模式的后缀自动机。</div>
<div>Apostolico-Crochemore算法是一种简单算法，最坏情况下也只需要3n/2次比较。<br></div>
<div>还有一种不那么幼稚（Not So Naive）的算法，最坏情况下是n平方，但是预处理过程的时间和空间均为常数，而且平均情况下的性能非常接近线性。</div>
<div><br></div>
<div><span class=Apple-style-span style="FONT-WEIGHT: bold">从右到左</span></div>
<div>BM算法被认为是通常应用中最有效率的算法了，它或者它的简化版本常用于文本编辑器中的搜索和替换功能，对于非周期性的模式而言，3n是这种算法的比较次数上界了，不过对于周期性模式，它最坏情况下需要n的二次方。</div>
<div>BM算法的一些变种避免了原算法的二次方问题，比较高效的有：Apostolico and Giancarlo算法、Turbo BM算法和Reverse Colussi算法。</div>
<div>实验的结果表明，Quick Search算法（BM的一个变种）以及基于后缀自动机的Reverse Factor和Turbo Reverse Factor算法算是实践中最有效的算法了。<br></div>
<div>Zhu and Takaoka算法和BR算法也是BM的变种，它们则需要<span class=Apple-style-span style="FONT-FAMILY: 'Times New Roman'">O(&#963;</span><span class=Apple-style-span style="FONT-FAMILY: 'Times New Roman'"><span class=Apple-style-span style="VERTICAL-ALIGN: super">2</span></span><span class=Apple-style-span style="FONT-FAMILY: 'Times New Roman'">)</span>的额外空间。</div>
<div><br></div>
<div><span class=Apple-style-span style="FONT-WEIGHT: bold">特殊顺序</span></div>
<div>最先达到空间线性最优的是Galil-Seiferas和Two Way算法，它们把模式分为两部分，先从左到右搜索右边的部分，如果没有失配，再搜索左边的部分。</div>
<div>Colussi和Galil-Giancarlo算法将模式位置分为两个子集，先从左至右搜索第一个子集，如果没有失配，再搜索剩下的。Colussi算法作为KMP算法的改进，使得最坏情况下只需要3n/2次比较，而Galil-Giancarlo算法则通过改进Colussi算法的一个特殊情况，把最坏比较次数减少到了4n/3。</div>
<div>最佳失配和M最大位移算法分别根据模式的字符频率和首字位移，对模式位置进行排序。<br></div>
<div>Skip Search，KMP Skip Search和Alpha Skip Search算法运用&#8220;桶&#8221;的方法来决定模式的起始位置。<br></div>
<div><br></div>
<div><span class=Apple-style-span style="FONT-WEIGHT: bold">任意顺序</span></div>
<div><span class=Apple-style-span style="FONT-WEIGHT: bold"><span class=Apple-style-span style="FONT-WEIGHT: normal">Horspool算法也是BM的一个变种，它使用一种移位函数，而与字符比较顺序不相干。还有其他的变种如：</span><span class=Apple-style-span style="FONT-WEIGHT: normal">Quick Search算法，</span><span class=Apple-style-span style="FONT-WEIGHT: normal">Tuned Boyer-Moore算法，</span><span class=Apple-style-span style="FONT-WEIGHT: normal">Smith算法，</span><span class=Apple-style-span style="FONT-WEIGHT: normal">Raita算法。</span><br></span></div>
<div><br></div>
<div>在接下来的章节中，我们会给出上面这些算法的实现。我们把字母表限定为ASCII码或者它的任意子集，编程语言用C，这就意味着数组索引是从0开始，而字符串以NULL结尾。</div>
<div><br></div>
<div>（<span class=Apple-style-span style="COLOR: rgb(51,51,153)">第一章完。好像这些算法被挨个夸了个遍，反而不知道该选哪一种了，老外介绍别人的东西时就是这样，尽来虚的。</span>）</div>
<img src ="http://www.cppblog.com/tgh621/aggbug/66593.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2008-11-11 13:05 <a href="http://www.cppblog.com/tgh621/archive/2008/11/11/66593.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>