**matrix67神牛
最後的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演算法詳解
模式匹配的kmp演算法詳解 這種由d.e.knuth,j.h.morris和v.r.pratt同時發現的改進的模式匹配演算法簡稱為kmp演算法。大概學過資訊學的都知道,是個比較難理解的演算法,今天特把它搞個徹徹底底明明白白。注意到這是乙個改進的演算法,所以有必要把原來的模式匹配演算法拿出來,其實理解...
KMP演算法詳解
kmp演算法即knuth morris pratt演算法,是模式匹配的一種改進演算法,因為是名字中三人同時發現的,所以稱為kmp演算法。因為偶然接觸到有關kmp的問題,所以上網查了一下next陣列和 nextval陣列的求法,卻沒有找到,只有在csdn的資料檔案裡找到了next陣列的簡單求法 根據書...
KMP演算法詳解
相信很多人 包括自己 初識kmp演算法的時候始終是丈二和尚摸不著頭腦,要麼完全不知所云,要麼看不懂書上的解釋,要麼自己覺得好像心裡了解kmp演算法的意思,卻說不出個究竟,所謂知其然不知其所以然是也。經過七八個小時地仔細研究,終於感覺自己能說出其所以然了,又覺得資料結構書上寫得過於簡潔,不易於初學者接...