KMP算法詳解 網絡上轉的。。。仰慕此人

原網址http://www.matrix67.com/blog/archives/115?

如果機房馬上要關門了,或者你急著要和MM約會,請直接跳到第六個自然段。

????我們這里說的KMP不是拿來放電影的(雖然我很喜歡這個軟件),而是一種算法。KMP算法是拿來處理字符串匹配的。換句話說,給你兩個字符串,你需要回答,B串是否是A串的子串(A串是否包含B串)。比如,字符串A="I'm matrix67",字符串B="matrix",我們就說B是A的子串。你可以委婉地問你的MM:“假如你要向你喜歡的人表白的話,我的名字是你的告白語中的子串嗎?”
????解決這類問題,通常我們的方法是枚舉從A串的什么位置起開始與B匹配,然后驗證是否匹配。假如A串長度為n,B串長度為m,那么這種方法的復雜度是O (mn)的。雖然很多時候復雜度達不到mn(驗證時只看頭一兩個字母就發現不匹配了),但我們有許多“最壞情況”,比如,A= "aaaaaaaaaaaaaaaaaaaaaaaaaab",B="aaaaaaaab"。我們將介紹的是一種最壞情況下O(n)的算法(這里假設 m<=n),即傳說中的KMP算法。
????之所以叫做KMP,是因為這個算法是由Knuth、Morris、Pratt三個提出來的,取了這三個人的名字的頭一個字母。這時,或許你突然明白了AVL 樹為什么叫AVL,或者Bellman-Ford為什么中間是一杠不是一個點。有時一個東西有七八個人研究過,那怎么命名呢?通常這個東西干脆就不用人名字命名了,免得發生爭議,比如“3x+1問題”。扯遠了。
????個人認為KMP是最沒有必要講的東西,因為這個東西網上能找到很多資料。但網上的講法基本上都涉及到“移動(shift)”、“Next函數”等概念,這非常容易產生誤解(至少一年半前我看這些資料學習KMP時就沒搞清楚)。在這里,我換一種方法來解釋KMP算法。

????假如,A="abababaababacb",B="ababacb",我們來看看KMP是怎么工作的。我們用兩個指針i和j分別表示,A[i-j+ 1..i]與B[1..j]完全相等。也就是說,i是不斷增加的,隨著i的增加j相應地變化,且j滿足以A[i]結尾的長度為j的字符串正好匹配B串的前 j個字符(j當然越大越好),現在需要檢驗A[i+1]和B[j+1]的關系。當A[i+1]=B[j+1]時,i和j各加一;什么時候j=m了,我們就說B是A的子串(B串已經整完了),并且可以根據這時的i值算出匹配的位置。當A[i+1]<>B[j+1],KMP的策略是調整j的位置(減小j值)使得A[i-j+1..i]與B[1..j]保持匹配且新的B[j+1]恰好與A[i+1]匹配(從而使得i和j能繼續增加)。我們看一看當 i=j=5時的情況。

????i = 1 2 3 4?5?6 7 8 9 ……
????A = a b a b?a?b a a b a b …
????B = a b a b?a?c b
????j = 1 2 3 4?5?6 7

????此時,A[6]<>B[6]。這表明,此時j不能等于5了,我們要把j改成比它小的值j'。j'可能是多少呢?仔細想一下,我們發現,j'必須要使得B[1..j]中的頭j'個字母和末j'個字母完全相等(這樣j變成了j'后才能繼續保持i和j的性質)。這個j'當然要越大越好。在這里,B [1..5]="ababa",頭3個字母和末3個字母都是"aba"。而當新的j為3時,A[6]恰好和B[4]相等。于是,i變成了6,而j則變成了 4:

????i = 1 2 3 4 5?6?7 8 9 ……
????A = a b a b a?b?a a b a b …
????B =???? a b a?b?a c b
????j =???? 1 2 3?4?5 6 7

????從上面的這個例子,我們可以看到,新的j可以取多少與i無關,只與B串有關。我們完全可以預處理出這樣一個數組P[j],表示當匹配到B數組的第j個字母而第j+1個字母不能匹配了時,新的j最大是多少。P[j]應該是所有滿足B[1..P[j]]=B[j-P[j]+1..j]的最大值。
????再后來,A[7]=B[5],i和j又各增加1。這時,又出現了A[i+1]<>B[j+1]的情況:

????i = 1 2 3 4 5 6?7?8 9 ……
????A = a b a b a b?a?a b a b …
????B =???? a b a b?a?c b
????j =???? 1 2 3 4?5?6 7

????由于P[5]=3,因此新的j=3:

????i = 1 2 3 4 5 6?7?8 9 ……
????A = a b a b a b?a?a b a b …
????B =???????? a b?a?b a c b
????j =???????? 1 2?3?4 5 6 7

????這時,新的j=3仍然不能滿足A[i+1]=B[j+1],此時我們再次減小j值,將j再次更新為P[3]:

????i = 1 2 3 4 5 6?7?8 9 ……
????A = a b a b a b?a?a b a b …
????B =?????????????a?b a b a c b
????j =?????????????1?2 3 4 5 6 7

????現在,i還是7,j已經變成1了。而此時A[8]居然仍然不等于B[j+1]。這樣,j必須減小到P[1],即0:

????i = 1 2 3 4 5 6?7?8 9 ……
????A = a b a b a b?a?a b a b …
????B =?????????????? a b a b a c b
????j =?????????????0?1 2 3 4 5 6 7

????終于,A[8]=B[1],i變為8,j為1。事實上,有可能j到了0仍然不能滿足A[i+1]=B[j+1](比如A[8]="d"時)。因此,準確的說法是,當j=0了時,我們增加i值但忽略j直到出現A[i]=B[1]為止。
????這個過程的代碼很短(真的很短),我們在這里給出:

j:=0;
for i:=1 to n do
begin
?? while (j>0) and (B[j+1]<>A[i]) do j:=P[j];
?? if B[j+1]=A[i] then j:=j+1;
?? if j=m then
?? begin
??????writeln('Pattern occurs with shift ',i-m);
??????j:=P[j];
?? end;
end;


????最后的j:=P[j]是為了讓程序繼續做下去,因為我們有可能找到多處匹配。
????這個程序或許比想像中的要簡單,因為對于i值的不斷增加,代碼用的是for循環。因此,這個代碼可以這樣形象地理解:掃描字符串A,并更新可以匹配到B的什么位置。

????現在,我們還遺留了兩個重要的問題:一,為什么這個程序是線性的;二,如何快速預處理P數組。
????為什么這個程序是O(n)的?其實,主要的爭議在于,while循環使得執行次數出現了不確定因素。我們將用到時間復雜度的攤還分析中的主要策略,簡單地說就是通過觀察某一個變量或函數值的變化來對零散的、雜亂的、不規則的執行次數進行累計。KMP的時間復雜度分析可謂攤還分析的典型。我們從上述程序的j 值入手。每一次執行while循環都會使j減小(但不能減成負的),而另外的改變j值的地方只有第五行。每次執行了這一行,j都只能加1;因此,整個過程中j最多加了n個1。于是,j最多只有n次減小的機會(j值減小的次數當然不能超過n,因為j永遠是非負整數)。這告訴我們,while循環總共最多執行了n次。按照攤還分析的說法,平攤到每次for循環中后,一次for循環的復雜度為O(1)。整個過程顯然是O(n)的。這樣的分析對于后面P數組預處理的過程同樣有效,同樣可以得到預處理過程的復雜度為O(m)。
????預處理不需要按照P的定義寫成O(m^2)甚至O(m^3)的。我們可以通過P[1],P[2],...,P[j-1]的值來獲得P[j]的值。對于剛才的B="ababacb",假如我們已經求出了P[1],P[2],P[3]和P[4],看看我們應該怎么求出P[5]和P[6]。P[4]=2,那么P [5]顯然等于P[4]+1,因為由P[4]可以知道,B[1,2]已經和B[3,4]相等了,現在又有B[3]=B[5],所以P[5]可以由P[4] 后面加一個字符得到。P[6]也等于P[5]+1嗎?顯然不是,因為B[ P[5]+1 ]<>B[6]。那么,我們要考慮“退一步”了。我們考慮P[6]是否有可能由P[5]的情況所包含的子串得到,即是否P[6]=P[ P[5] ]+1。這里想不通的話可以仔細看一下:

????????1 2 3 4 5 6 7
????B = a b a b a c b
????P = 0 0 1 2 3 ?

????P[5]=3是因為B[1..3]和B[3..5]都是"aba";而P[3]=1則告訴我們,B[1]、B[3]和B[5]都是"a"。既然P[6]不能由P[5]得到,或許可以由P[3]得到(如果B[2]恰好和B[6]相等的話,P[6]就等于P[3]+1了)。顯然,P[6]也不能通過P[3]得到,因為B[2]<>B[6]。事實上,這樣一直推到P[1]也不行,最后,我們得到,P[6]=0。
????怎么這個預處理過程跟前面的KMP主程序這么像呢?其實,KMP的預處理本身就是一個B串“自我匹配”的過程。它的代碼和上面的代碼神似:

P[1]:=0;
j:=0;
for i:=2 to m do
begin
?? while (j>0) and (B[j+1]<>B[i]) do j:=P[j];
?? if B[j+1]=B[i] then j:=j+1;
?? P[i]:=j;
end;


????最后補充一點:由于KMP算法只預處理B串,因此這種算法很適合這樣的問題:給定一個B串和一群不同的A串,問B是哪些A串的子串。

????串匹配是一個很有研究價值的問題。事實上,我們還有后綴樹,自動機等很多方法,這些算法都巧妙地運用了預處理,從而可以在線性的時間里解決字符串的匹配。我們以后來說。

????昨天發現一個特別暈的事,知道怎么去掉BitComet的廣告嗎?把界面語言設成英文就行了。
????還有,金山詞霸和Dr.eye都可以去自殺了,Babylon素王道。

Matrix67原創
轉貼請注明出處

轉載于:https://www.cnblogs.com/0803yijia/archive/2012/07/20/2601111.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/260614.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/260614.shtml
英文地址,請注明出處:http://en.pswp.cn/news/260614.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

求一個二維數組外圍元素之和_C++數組作為函數的參數(學習筆記:第6章 04)...

數組作為函數的參數[1]數組元素作實參&#xff0c;與單個變量一樣。數組名作參數&#xff0c;形、實參數都應是數組名&#xff08;實質上是地址&#xff0c;關于地址詳見后續章節&#xff09;&#xff0c;類型要一樣&#xff0c;傳送的是數組首地址。對形參數組的改變會直接影響…

android p wifi一直在掃描_Android再次解讀螢石云視頻

點擊上方藍字關注 ??前言我之前寫過一篇螢石云的集成文章&#xff0c;很多人問我有沒有demo&#xff0c; 今天我再次總結一下&#xff0c; 并加個些功能。集成步驟視頻預覽播放視頻放大縮小視頻的質量切換截圖之前的文章大家可以看下面的鏈接&#xff1a;https://mp.weixin.q…

第5章 Python 數字圖像處理(DIP) - 圖像復原與重建7 - 周期噪聲 余弦噪聲生成方法

標題周期噪聲周期噪聲 周期噪聲通常是在獲取圖像期間由電氣或機電干擾產生的 def add_sin_noise(img, scale1, angle0):"""add sin noise for imageparam: img: input image, 1 channel, dtypeuint8param: scale: sin scaler, smaller than 1, will enlarge, …

python寫文字方法_Transcrypt: 用Python寫js的方法

Transcrypt是一個很有意思的工具&#xff1a; 它讓你告別手寫繁復的JavaScript代碼&#xff0c;使用相對簡明清晰的Python代替這一工作。 之后使用這個工具&#xff0c;可以把Python編寫的代碼轉換成JavaScript。 1. 為什么不直接寫JavsScript? JavaScript本身不算是很難的編程…

第5章 Python 數字圖像處理(DIP) - 圖像復原與重建8 - 估計噪聲參數

標題估計噪聲參數估計噪聲參數 周期噪聲的參數通常是通過檢測圖像的傅里葉譜來估計的。 只能使用由傳感器生成的圖像時&#xff0c;可由一小片恒定的背景灰度來估計PDF的參數。 來自圖像條帶的數據的最簡單用途是&#xff0c;計算灰度級的均值和方差。考慮由SSS表示的一個條…

python 隨機獲取數組元素_Python創建二維數組的正確姿勢

List &#xff08;列表&#xff09;是 Python 中最基本的數據結構。在用法上&#xff0c;它有點類似數組&#xff0c;因為每個列表都有一個下標&#xff0c;下標從 0 開始。因此&#xff0c;我們可以使用 list[1] 來獲取下標對應的值。如果我們深入下列表的底層原理&#xff0c…

Qt學習筆記1

1.Qt引用API時&#xff0c;QString到LPCWSTR的轉換&#xff1a; ::GetPrivateProfileIntW(QString(tr("相關設置")).utf16(),QString(tr("時間間隔")).utf16(),5,filePath.utf16())); 2.引用LPRECT時&#xff1a; RECTappRect; ::GetWindowRect(AppWnd,(LP…

在ubunut下使用pycharm和eclipse進行python遠程調試

我比較喜歡Pycharm&#xff0c;因為這個是JetBrains公司出的python IDE工具&#xff0c;該公司下的java IDE工具——IDEA&#xff0c;無論從界面還是操作上都甩eclipse幾條街&#xff0c;但項目組里有些人使用eclipse比較久了&#xff0c;一時讓他們轉pycharm比較困難&#xff…

CSS:頁腳緊貼底部

2019獨角獸企業重金招聘Python工程師標準>>> 我的練習來源于《CSS揭秘》這本書第7章-41緊貼底部的頁腳這部分內容以及書中提到的鏈接。 方案一 描述&#xff1a;以下方案簡單、干凈、現代并且沒有hack&#xff0c;適用于IE8, Chrome, Firefox, Opera等瀏覽器&#x…

第5章 Python 數字圖像處理(DIP) - 圖像復原與重建9 - 空間濾波 - 均值濾波器 - 算術平均、幾何平均、諧波平均、反諧波平均濾波器

標題只存在噪聲的復原 - 空間濾波均值濾波器算術平均濾波器幾何均值濾波器諧波平均濾波器反&#xff08;逆&#xff09;諧波平均濾波器只存在噪聲的復原 - 空間濾波 僅被加性噪聲退化 g(x,y)f(x,y)η(x,y)(5.21)g(x, y) f(x, y) \eta(x, y) \tag{5.21}g(x,y)f(x,y)η(x,y)(5…

librosa能量_librosa與python_speech_features

在語音識別領域&#xff0c;比較常用的兩個模塊就是librosa和python_speech_features了。最近也是在做音樂方向的項目&#xff0c;借此做一下筆記&#xff0c;并記錄一些兩者的差別。下面是兩模塊的官方文檔LibROSA - librosa 0.6.3 documentation?librosa.github.ioWelcome t…

java Unicode轉碼

1 //中文轉UNICODE2 public static String chinaToUnicode(String str) {3 String result "";4 for (int i 0; i < str.length(); i) {5 int chr1 (char) str.charAt(i);6 if (chr1 > 19968 && ch…

oracle-備份工具exp-imp

雖然是按照用戶的方式導出的&#xff0c;但導入之前&#xff0c;還是必須要有相同的用戶存在&#xff0c;刪除用戶以后&#xff0c;是無法進行導入的 --重新創建回zlm用戶 SQL> create user zlm identified by zlm; 盡管zlm用戶的默認表空間是USERS&#xff0c;但是用imp導入…

繼承String?

不能繼承&#xff0c;因為 public final class String extends Objectimplements Serializable, Comparable<String>, CharSequence final修飾的類是不能被繼承的轉載于:https://www.cnblogs.com/crane-practice/p/3666006.html

python中字典數據的特點_Python數據類型(字典)

Python 字典(Dictionary) 字典是另一種可變容器模型&#xff0c;且可存儲任意類型對象。 字典的每個鍵值(key>value)對用冒號(:)分割&#xff0c;每個對之間用逗號(,)分割&#xff0c;整個字典包括在花括號({})中 ,格式如下所示&#xff1a; d {key1: value1, key2: value2}…

第5章 Python 數字圖像處理(DIP) - 圖像復原與重建10 - 空間濾波 - 統計排序濾波器 - 中值、最大值、最小值、中點、修正阿爾法均值濾波器

標題統計排序濾波器中值、最大值、最小值、中點 濾波器修正阿爾法均值濾波器統計排序濾波器 中值、最大值、最小值、中點 濾波器 f^(x,y)median{g(r,c)}(5.27)\hat{f}(x, y) \text{median} \{g(r,c)\} \tag{5.27}f^?(x,y)median{g(r,c)}(5.27) f^(x,y))max{g(r,c)}(5.28)\ha…

如何設置坐標原點值_氨氣檢測儀電化學原理及報警值如何設置

氨氣體檢測儀檢定規程&#xff1a;一般氨氣體檢測儀檢定規程主要是針對技術參數設定的一些標準&#xff0c;具體包含有規程的名稱和范圍、儀器示值誤差、充分性標準差、響應時間、穩定性、報警功能、流量控制器、檢定項目表、檢定操作有數值誤差、重復性、響應時間、穩定性等。…

統計信息及相關說明

統計信息&#xff1a;0 recursive calls20434 db block gets 317970511 consistent gets 0 physical reads 3759764 redo size 382 bytes sent via SQL*Net to client 1061 bytes received via SQL*Net from client 3 SQL*Ne…

Android橫豎屏切換的生命周期

關于Android手機橫豎屏切換時Activity的生命周期問題&#xff0c;網上有很多相似的文章&#xff0c;大多數都是說明在豎屏切換橫屏時Activity會重啟一次&#xff0c;而在橫屏切換豎屏時Activity會重啟兩次。 我本身不太理解這樣設計的意義&#xff0c;并且覺得新版本會解決這個…

python 隨機字符串_python生成隨機數、隨機字符串

python生成隨機數、隨機字符串 import random import string # 隨機整數&#xff1a; print random.randint(1,50) # 隨機選取0到100間的偶數&#xff1a; print random.randrange(0, 101, 2) # 隨機浮點數&#xff1a; print random.random() print random.uniform(1, 10) # 隨…