串的普通模式匹配演算法(bf演算法),大體思路是:模式串從主串的第乙個字元開始匹配,每匹配失敗,主串中記錄匹配進度的指標 i 都要進行 i-j+1 的回退操作(這個過程稱為「指標回溯」),同時模式串向後移動乙個字元的位置。一次次的迴圈,直到匹配成功或者程式結束。
kmp演算法相比於bf演算法,優勢在於:
故,kmp演算法稱為「快速模式匹配演算法」。
在模式串和主串匹配時,各有乙個指標指向當前進行匹配的字元(主串中是指標 i ,模式串中是指標 j ),在保證 i 指標不回溯的前提下,如果想實現功能,就只能讓 j 指標回溯。
j 指標回溯的距離,就相當於模式串向右移動的距離。 j 指標回溯的越多,說明模式串向右移動的距離越長。計算模式串向右移動的距離,就可以轉化成:當某字元匹配失敗後, j 指標回溯的位置。
對於乙個給定的模式串,其中每個字元都有可能會遇到匹配失敗,這時對應的 j 指標都需要回溯,具體回溯的位置其實還是由模式串本身來決定的,和主串沒有關係。
模式串中的每個字元所對應 j 指標回溯的位置,可以通過演算法得出,得到的結果相應地儲存在乙個陣列中(預設陣列名為 next )。
計算方法是:對於模式串中的某一字元來說,提取它前面的字串,分別從字串的兩端檢視連續相同的字串的個數,在其基礎上 +1 ,結果就是該字元對應的值。
每個模式串的第乙個字元對應的值為 0 ,第二個字元對應的值為 1 。例如:求模式串 「abcabac」 的 next 。前兩個字元對應的 0 和 1 是固定的。
對於字元 『c』 來說,提取字串 「ab」 ,『a』 和 『b』 不相等,相同的字串的個數為 0 ,0 + 1 = 1 ,所以 『c』 對應的 next 值為 1 ;
第四個字元 『a』 ,提取 「abc」 ,從首先 『a』 和 『c』 就不相等,相同的個數為 0 ,0 + 1 = 1 ,所以,『a』 對應的 next 值為 1 ;
第五個字元 『b』 ,提取 「abca」 ,第乙個 『a』 和最後乙個 『a』 相同,相同個數為 1 ,1 + 1 = 2 ,所以,『b』 對應的 next 值為 2 ;
第六個字元 『a』 ,提取 「abcab」 ,前兩個字元 「ab」 和最後兩個 「ab」 相同,相同個數為 2 ,2 + 1 = 3 ,所以,『a』 對應的 next 值為 3 ;
最後乙個字元 『c』 ,提取 「abcaba」 ,第乙個字元 『a』 和最後乙個 『a』 相同,相同個數為 1 ,1 + 1 = 2 ,所以 『c』 對應的 next 值為 2 ;
所以,字串 「abcabac」 對應的 next 陣列中的值為(0,1,1,1,2,3,2)。
上邊求值過程中,每次都需要判斷字串頭部和尾部相同字元的個數,而在編寫演算法實現時,對於某個字元來說,可以借用前乙個字元的判斷結果,計算當前字元對應的 next 值。
具體的演算法如下:
模式串t為(下標從1開始):「abcabac」
next陣列(下標從1開始):01
第三個字元 『c』 :由於前乙個字元 『b』 的 next 值為 1 ,取 t[1] = 『a』 和 『b』 相比較,不相等,繼續;由於 next[1] = 0,結束。 『c』 對應的 next 值為1;(只要迴圈到 next[1] = 0 ,該字元的 next 值都為 1 )
模式串t為:「abcabac」
next陣列(下標從1開始):011
第四個字元 』a『 :由於前乙個字元 『c』 的 next 值為 1 ,取 t[1] = 『a』 和 『c』 相比較,不相等,繼續;由於 next[1] = 0 ,結束。『a』 對應的 next 值為 1 ;
模式串t為: 「abcabac」
next陣列(下標從1開始):0111
第五個字元 』b』 :由於前乙個字元 『a』 的 next 值為 1 ,取 t[1] = 『a』 和 『a』 相比較,相等,結束。 『b』 對應的 next 值為:1(前乙個字元 『a』 的 next 值) + 1 = 2 ;
模式串t為:「abcabac」
next陣列(下標從1開始):01112
第六個字元 『a』 :由於前乙個字元 『b』 的 next 值為 2,取 t[2] = 『b』 和 『b』 相比較,相等,所以結束。『a』 對應的 next 值為:2 (前乙個字元 『b』 的 next 值) + 1 = 3 ;
模式串t為: 「abcabac」
next陣列(下標從1開始):011123
第七個字元 『c』 :由於前乙個字元 『a』 的 next 值為 3 ,取 t[3] = 『c』 和 『a』 相比較,不相等,繼續;由於 next[3] = 1 ,所以取 t[1] = 『a』 和 『a』 比較,相等,結束。『a』 對應的 next 值為:1 ( next[3] 的值) + 1 = 2 ;
模式串t為:「abcabac」
next陣列(下標從1開始):0111232
演算法實現:
#include
#include
void
next
(char
*t,int
*next)
else
}}
注意:在此程式中,next 陣列使用的下標初始值為 1 ,next[0] 沒有用到(也可以存放 next 陣列的長度)。而串的儲存是從陣列的下標 0 開始的,所以程式中為 t[i-1] 和 t[j-1]。先看一下 kmp 演算法執行流程(假設主串:ababcabcacbab,模式串:abcac)。
第一次匹配:
匹配失敗,i 指標不動,j = 1(字元『c』的next值);
第二次匹配:
相等,繼續,直到:
匹配失敗,i 不動,j = 2 ( j 指向的字元 『c』 的 next 值);
第三次匹配:
相等,i 和 j 後移,最終匹配成功。
使用普通演算法,需要匹配 6 次;而使用 kmp 演算法,則只匹配 3 次。實現**:
int
kmp(
char
*s,char
*t)else}if
(j >
strlen
(t))
return0;
}
#include
#include
void
next
(char
*t,int
*next)
else}}
intkmp
(char
*s,char
*t)else}if
(j >
strlen
(t))
return0;
}int
main()
執行結果:
6注意:kmp 演算法的關鍵在於 next 陣列的確定,其實對於上邊的kmp演算法中的next陣列,不是最精簡的,還可以簡化。
例如:
模式串t:a b c a c在模式串「abcac」中,有兩個字元 『a』,我們假設第乙個為 a1,第二個為 a2。在程式匹配過程中,如果 j 指標指向 a2 時匹配失敗,那麼此時,主串中的 i 指標不動,j 指標指向 a1 ,很明顯,由於 a1==a2,而 a2!=s[i],所以 a1 也肯定不等於 s[i]。next :0 1 1 1 2
為了避免不必要的判斷,需要對 next 陣列進行精簡,對於「abcac」這個模式串來說,由於 t[4] == t[next[4]] ,所以,可以將next陣列改為:
模式串t:a b c a c這樣簡化,如果匹配過程中由於 a2 匹配失敗,那麼也不用再判斷 a1 是否匹配,因為肯定不可能,所以直接繞過 a1,進行下一步。next :0 1 1 0 2
實現**:
void
next
(char
*t,int
*next)
else
}else
}}
使用精簡過後的 next 陣列在解決例如模式串為「aaaaaaab」這類的問題上,會減少很多不必要的判斷次數,提高了kmp演算法的效率。
例如:精簡前為 next1,精簡後為 next2:
模式串:a a a a a a a bkmp 演算法,之所以比 bf 演算法快的根本原因在於:kmp 演算法其實也和 bf 演算法一樣,都是從主串開頭開始匹配,但是在匹配過程中,kmp演算法記錄了一些必要的資訊。根據這些資訊,在後續的匹配過程中,跳過了一些無意義的匹配過程。next1:0 1 2 3 4 5 6 7
next2:0 0 0 0 0 0 0 7
快速模式匹配演算法(KMP)
恐怕現在用過電腦的人,一定都知道大部分帶文字編輯功能的軟體都有乙個快捷鍵ctrl f 吧 比如word 這個功能主要來完成 查詢 替換 和 全部替換 功能的,其實這就是典型的模式匹配的應用,即在文字檔案中查詢串。1.模式匹配 模式匹配的模型大概是這樣的 給定兩個字串變數s和p,其中s成為目標串,其中...
模式匹配演算法(KMP演算法詳解)
從主串s goodgoogle 中找到t google 這個子串的位置 public static intindex string s,string t else if j t.length else 思路 利用已經部分匹配這個有效資訊,保持i指標不回溯,通過修改j指標,讓模式串盡量地移動到有效的位...
模式匹配 KMP演算法詳解
kmp 模式匹配演算法 include include using namespace std 計算模式串的next陣列 模式串既做主串,又做模式串,進行匹配 時間複雜度為o m m為模式串的長度 void countnext char strpattern,int len,int next els...