字符串匹配shiftand算法
令人驚嘆的Shift-And/Shift-Or
寫在前面:Shift-And/Shift-Or是如此令人驚嘆的算法,在KMP基礎上開始一段神奇之旅。
?
目的:以Shift-And算法為載體,試圖在減少思維斷層情況下學習作者算法思想。
?
目錄:
? ????1:主要思想
? ? ? ??2:算法介紹
???3:構建輔助表B
? ?4:容器創建和更新
? ?5:過程展示
? ?6: Shift-And VS KMP,展示Shift-And令人驚嘆之處
? ?7:在KMP的基礎上,揭示Shift-And的神器:位并行(精髓)
第一步:主要思想(可以先看第3-5步,更容易理解)
???Shift-And算法和KMP算法一樣,是基于前綴來進行字符串匹配。但是它的算法思想要比KMP思路簡單很多。它主要是維護了一個集合,該集合中保存的是所有既是模式串的前綴同時又是目標串的后綴的字符串。每次讀入一個新的文本,本算法就利用位并行的方法更新該集合(神奇之處)。該集合用一個位掩碼D進行表示:dm...d1表示(m表示的是模式串的大小)。
第二步:算法介紹(可以先看第3-5步,更容易理解)
? ??D的第j位為1的時候(此時成Dj是活動的),表示模式串前綴的p1…pj同時也是目標串t1…ti后綴.而當dm是1的時候,表示有一個匹配成功。當讀入目標串的下一個字符t(i+1)的時候,需要對D進行更新為D’當且僅當D的第j位是活動的,并且t(i+1)和p(j+1)相同的時候,此時可以利用位并行的方法在常數時間內對D進行更新。
第三步:構建輔助表B
???集合B記錄模式串中每一個字符位掩碼bm…b1.如果pj=x,則B[x]的第j為設為1.否則為0.
舉例1:模式串announce共8位
?
?同理可以得到B(其中模式串中不包含的字符*設為00000000)
?
?第四步:容器創建和更新
???????對于容器D,初始化為00000000(0m?:前m位全為0).表示當前還沒有即使模式串前綴又是目標串后綴。
?
???????當讀入一個新的目標串字符t(i+1),可以以如下公式進行更新。
?第五步:過程展示
???????下面是整個過程:目標串是’annual_announce’?模式串announce
? ??其實這個例子個人覺得并不是很理想,雖然它能說明情況,但是很難從這個例子的過程中體會到這個過程奇妙發生的根本所在。
?
例子2:
?
目標串是cbcbcbaefd??模式串是cbcba
?
建表B:
?如果你看明白了,就會發現上述做法真的很奇妙。
第5)步中,讀取了c,但是模式串中的確實a,在沒有讀取c之前,結果是01010,這個的意思是模式串前4個字符前兩個字符cb和前4個字符cbcb既是模式串的前綴,同時又是目標串的后綴。當讀入第5個字符c后,經過更新D后變成了00101,這個結果表示前5個字符中,只有第1個字符c和前3個字符cbc既是模式串的前綴又是目標串的后綴。
?
這就是它的厲害之處,讀入一個新的字符之后,經過這樣3個步驟,就計算出來當前模式串前5個字符中所有的前綴(同時是目標串的后綴)。
?
也許這樣還表現不太明顯,但是如果你很熟悉KMP算法,因為KMP的貢獻在于它并不進行回溯同時很巧妙的利用迭代改變指針j。如果說kmp巧妙,它確實是,但是和Shift-And相比,真是小巫見大巫。因為kmp是用迭代,有可能需要迭代很多次,才能達到效果,此處只是一次位并行操作,就達到了kmp的效果。效率大大的提高。
?
第六步:Shift-And ?VS ?KMP,展示Shift-And令人驚嘆之處
?
KMP算法的精髓在于不回溯并采用巧妙的迭代方法得到next數組,將時間復雜度理論上降到了o(n)。
?
如果想清楚了解KMP,可以參考
?
http://wlh0706-163-com.iteye.com/blog/1843923
?
以下面這個案例再次進行分析:
?
?
?當第26個字符,c和f匹配失敗的時候,kmp使用的方法是:找到了模式串中前25個字符的所有的既是模式串的前綴同時又是目標串的后綴。
?
?
?
?找到了這4對前綴?a,aca,acabaca,acabacabaca.
?
??上圖中第1步:由于最大的前綴acabacabaca的后面一個字符t和第26個字符c并不匹配,執行第2步。
?
?
上圖中第2步:由于第二大的前綴acabaca(同時是最大前綴acabacabaca的最大前綴,這是kmp算法實施迭代技巧的的根本性質)后面的一個字符b和第26個字符c并不匹配,執行第3步。
?
上圖中第3步同樣面臨這c和b不等的情況,執行第4步。
?
上圖中第4步:由于前綴a后一個字符c和第26個字符相同,此時指向目標串的指針i= 26和執行模式串的指針j由原來的26經過一系列改變(12,8,2)最終為2.?然后i++和j++開始匹配下一個字符.
?
代碼:
?(本段代碼在上篇文章中有)
?
? ??然而在看一下shift-and算法是如何找到這個kmp進行了4次迭代才找到的第2位的c的。
?
此例中:
B[c] =
當比較完第25個字符之后
D? = ?
(這個是根據D的定義結合上述圖片展示的4對前綴寫出來的)
運算過程:
?只是一次運算就計算出了kmp中需要4次迭代才能計算出來的結果。
那么Shift-And是如何需要1次就做到的KMP?算法4次才能做到的效果呢?下面來演示這個過程。
第七步:在KMP的基礎上,揭示Shift-And的神器:位并行(精髓)
?
?
? ? ??再來看張圖:其實這4步操作,目的只有一個,就是拿目標串的c和模式串的4對前綴的后一個字符相比較(其他字符都不需要比較)。即c和t,b,b,c相比較。
而t,b,b,c的位置是什么?12,8,4,2。
再看看Shift-And中的D左移一位之后是什么呢?
?? 你可以清楚的看到上面的數字,奇妙之處就在這里。
?(注:26實際上已經比較過了,就是第1次比較c和f)
這個只是找到了要和c比較的位置,下一步就是比較這些位置是不是c,所以才有了shift-And算法中第三步:相與操作。即從容器B中提取出來c出現的所有的位置即位掩碼B[c]
? ? ??其實這里為什么能用相與操作呢?如果想要深入理解相與的妙處,可以先看一個簡單的案例
http://wlh0706-163-com.iteye.com/blog/1393631??這里的c和12,8,4,2這些位置的比較實際相同或者不相同的比較,而這個正式‘與’的本質特征。
???走到這一步完了嗎?當然還沒有,不過已經接近重點了。那就是Shift-And的第2步?加1是為了什么?
? ?其實這個沒有什么神秘之處,只是如果你對kmp不是特別熟,即便是很熟悉也有可能會忘記。就是在這幅圖中
?
? ??我們幸運的是第4步發現了相同的c,但是如果沒有發現呢?例如目標串的第26個字符不是c而是a呢?我們就會有第5步,和它比較的是誰呢?是第1個字符。這個意思是第4步失敗,將要尋找c前面即a的最大前綴再加1的位置(和前3次一樣)而我們默認a的最大前綴+1等于0+1=1.也就是很多其他博客中引用原作者說的那句話:空字符串也是目標串的后綴。a的前綴還有一個空字符。實際上本例也能看出來,第目標串第26個字符是a,顯然有和模式串第一個字符a比較的需要。
?
???????現在如果看懂了整個過程,可以去分析第一步和第二步所說是如此經典。
???????不得不說,Shift-And算法是看透了KMP基于前綴匹配的本質特征,即比較的時候實際上是非黑即白的比較,使用01這種方法實在令人佩服。這個算法效率一般是KMP的2倍以上。當然,基于位并行操作實際和機器字長有關系,比如32位限制或者其他,但是它在絕大部分機器上都能運行,除非機器字長為8(這種機器應該年齡很大了吧..),你所查詢的是大于8位。
???????至于Shift-Or,它所用的原理是一樣的,只不過更加富有技巧性,使用了反碼和取反等操作,加速D’的計算。有興趣可以自行研究。如果看懂本例,在用點心相信看懂Shift-Or沒什么問題。
? ? ? ??感謝Shift-And算法帶我們走了一段神奇之旅!!!Wonderful!!
總結
以上是生活随笔為你收集整理的字符串匹配shiftand算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 寿司加盟多少钱啊?
- 下一篇: ccf Markdown