隨筆 - 100  文章 - 50  trackbacks - 0
          <2012年4月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          常用鏈接

          留言簿(3)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          收藏夾

          我收藏的一些文章!

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          經典單模式匹配算法:KMPBM;經典多模式匹配算法:ACWu-Manber。貌似實用中,KMPCstrstr()效率相當,而BM能快上3x-5x。于是小女不才花了小天的功夫來研究這個BM算法。BM如何快速匹配模式?它怎么跳躍地?我今兒一定要把大家伙兒講明白了,講不明白您佬跟帖,我買單,包教包會。

          模式,記為pat,用j作為索引; 文本,記為string(或text),用i作為索引。

           

          Input: pat, string

          Algorithm: BM,在string中進行pat匹配。

          Output: 匹配上則返回匹配地址,否則返回-1

           

          1

           

          1是一簡單示意圖。左對齊patstring,小指針(記為p)指向對齊后的右end,開始比對。如果pat[p]= string[p],那么小指針往左挪(挪到左end說明匹配上了),否則就要滑動pat進行重新對齊,重新對齊后,小指針當然也要跟著溜到末位進行重新比對。那么究竟怎么個滑法?分四個case

           

          1.       末位不匹配,且string[p]pat中不存在,那么pat可以一下子右移patlen個單位。因為你一個一個右移只是徒勞,沒人跟string[i]能匹配上。比如,圖1FT不匹配,且Fpat中不存在,那么我們可以把pat右滑patlen,小指針也跟著移至末位,移動后如圖2所示。

           

           

          2

           

          2.       末位不匹配,但string[p]pat中存在(如果有多個,那就找最靠右的那個),距離pat右端為delta1。那么右移pat使得它們對齊。比如,圖2中減號與T不匹配,但減號存在于pat中,數數知道delta1=4,那就右移pat使得兩個減號對上,移動后如圖3所示。

           

           

          3

           

          總結:從12可以得到,

          dealta1 = patlen,  string[p]patlen中不存在

                = patlen – 最右邊那個string[p]的位置,   string[p]patlen中存在

           

          delta1()是所有字符的函數,例如patstring對應26個字母,那么dealta1(‘a’)…dealta1(‘z’)。只需掃描一下pat,就能記錄下值了。別地兒管這個叫壞字符規則

           

          3.       m位都匹配上了(m<patlen),但未匹配完,如圖4中的三個示例,末m (m=4)位匹配上了,小指針指向的兩個字符都發生了mismatch,記為mismatched char

          1)        4中示例1string中的cpat中的最右出現居然還在小指針靠后的位置,總不至于為了讓stringcpat中最右c匹配上就把pat往回倒滑一個位置吧,才不要那么瓜,遇到這種情況就讓pat往右滑k=1個位置好了,此時小指針為了滑至最后需要滑k+m=5個位置。

          2)        4中示例2stringcpat中的最右出現在小指針前面,那好吧,就讓此a跟彼a對齊吧。即讓pat向右滑k=delta1(‘a’)-m=6-4=2個位置,此時小指針為了滑至最后需要滑k+m={dealta1(‘a’)-m}+m=dealta1(‘a’)=6個位置。

          3)        4中示例3stringypat中未出現。那么將patlen向右移k=delta1(‘y’)-m=6-4=2個位置,此時小指針為了滑至最后需要滑dealta1(‘y’)=6個位置。

           

           

          4

           

          總結:從3可以得到,

          pat右移位數 = 1  當示例1

                      = k =delta1(‘char’)-m 當示例23.

          String右移位數 = k+m

           

          4.         照著3那么移挺對也挺好地,但某些情況下,如圖7的情況,能不能讓pat右移地更快呢?圖7示例1,按3的分析只能將pat右滑1位,實際上我們可以放心右滑pat成示例2的樣子,然后再將小指針移至末位開始匹配。

           

           

          7

           

          下面的部分會比較繞,請讀者用心看。圖7示例1,末m(m=3)位即abc匹配上了,記為subpat,那么pat中出現的最右abc且不由mismatched char引導的位置,記為末subpat重現位置,如gabcfabceabceabc重現位置應該是f引導的subpat,可以理解么?因為g引導的subpat不是最右的,倒數第2e引導的subpat是由mismatched char引導的。

          于是我們引入delta2(j)函數,j是發生mismatched的位置,我們記subpat重現位置rpr(j),那么pat應該右移k,相應地string右移k+m。如何計算k?

           

          預處理patj=1…patlen,那么rpr(j)是指以jmismatched的位置,以j+1…patlensubpat重現位置

          rpr(j) = max{k| k<=patlen && [pat(j+1) ... pat(patlen)]= [pat(k) ... pat(k+patlen-j-1)]

                    && (k<=1 || pat(k-1) != pat(j) }    rpr(patlen)=patlen

          其中對于“=”的判斷,要么pat(x)=pat(j)要么pat(x)=NULL要么pat(y)=NULL

          舉個例子就明白了:

           

          下面解釋rpr(j)

           

          上圖您能接受么?呵呵,$表示空元素。例如j=1時,要跟pat[j+1]…pat[patlen]匹配,那么pat[k]…p[k+patlen-j-1]最多就是如圖所示,此時k+patlen-j-1=3k+9-1-1=3,于是k= -4k再大您可以試試,不好使了就。其它依此類推。讀者可練習求一下下面這個rpr(j)

           

           

          OK,如何求滑動距離k呢?現在小指針指在j的位置上,重現位置rpr(j),那么k=j+1-rpr(j),小指針需要挪至最后所以k+m={j+1-rpr(j)}+{patlen-j}=patlen+1-rpr(j),即有delta2(j)=patlen+1-rpr(j)

           

          總結:從34可以得到,

          m個元素已經匹配的情況,string需要右滑多少呢?計算delta1(string(i)),delta2(j),誰大取誰,就說滑的越多越好,反正都有匹配不上的理由。

           

          OK,現在給出算法偽碼,加油,就快結束了:

           

           

          實現上,可以更快一點。看到delta0()不要驚訝,它和delta1()基本相同,除了delta0(pat(patlen))被設置為>stringlen+patlen的一個數。因為12兩種case在匹配中遇到的頻率很高,我們抽出fast部分,匹配時間的70%-80%都在走fast部分。自己舉個例子把偽碼過一遍,不明白地方跟帖。

           

           

           

          別地兒都稱 壞字符規則” “好后綴規則,嘛回事?fatdog如是寫:

           

           

           

          哈哈,好不好笑?壞字符規則就是我們的delta1(char)計算,好后綴規則就是我們的delta2(j)計算,本來就一碼事兒。

          //預處理

          計算bmGS[]bmBC[]表;//BMGood SuffixBad Character

          while(text<textend)

          {

               //從當前匹配點text開始匹配關鍵詞

               for(i=m;(i>=0)&&(text[i]=pattern[i]);i--)

                   ;

               if(i<0)

               {

                   //匹配成功

                   報告一個成功的匹配;

                   text+=bmGS[0];//選擇下一個匹配入口點

               }

               else  //匹配失敗,此時i指示著不匹配的位置點text[i]!=pat[i]

               {

                   //使用兩種啟發式方法選擇下一個匹配入口點

                   text+=Max(bmGS[i]-m+1,bmBC[i]);

               }

          }

          BM通常是sublinear的復雜度,最好O(n/m),最壞O(n)。一般會匹配string中的c*(i+patlen)個字符,其中c<1,并且patlen越大c越小,通常在longer patBM表現更出色。

          posted on 2012-04-28 22:59 fly 閱讀(449) 評論(0)  編輯  收藏 所屬分類: 算法
          主站蜘蛛池模板: 瑞金市| 瑞安市| 黄陵县| 青海省| 红安县| 九寨沟县| 东至县| 金溪县| 牙克石市| 宣恩县| 长兴县| 黄骅市| 峨山| 浙江省| 安远县| 安福县| 商河县| 东莞市| 阿拉善右旗| 彭阳县| 津市市| 信丰县| 昭通市| 福清市| 乐山市| 阿城市| 合山市| 郯城县| 岐山县| 扬州市| 兴宁市| 嘉黎县| 扶风县| 垣曲县| 镶黄旗| 呼伦贝尔市| 理塘县| 上林县| 湖北省| 天全县| 赣州市|