KMP演算法(java實現)

2021-08-09 05:44:00 字數 3480 閱讀 7608

kmp演算法是由donald kunth、vaughan pratt、james h.morris三個人發明的,是一種複雜度很小的匹配字串的演算法。

給定兩個字串str和match,長度分別為n和m。實現乙個演算法,如果字串str中含有子串match,則返回match在str中的開始位置,不含有則返回-1。

正常的思路,從str[0]開始匹配,如果和match[0]相等,則匹配它們之後的字元是否相等,如果全部相等則含有。但是如果匹配到一定程度發現後邊的字元不相等,則再從str[1]開始匹配。不排除str的每個字元都要做匹配的可能性,比如str=」aaaaaaaaab」, match=」aaab」,這樣的複雜度為o(n*m)非常高。每一次的檢查都是從零開始,所以顯得很麻煩。

這時候有個解決辦法,就是使用kmp演算法:

要生成match字串的nextarr陣列,陣列長度和字串長度相等,每乙個nextarr[i]都對應乙個數字。那麼這個nextarr[i]的含義是什麼?

在字串match[0]至match[i-1]中,有包含match[0]且不包含match[i-1]的字首子串,還有包含match[i-1]且不包含match[0]的字尾子串,兩者的最大匹配長度就是nextarr[i]的值。

舉例說明:aaaab這個字串,nextarr[4],字元b之前的就是aaaa。字首子串為前三個aaa,字尾子串為後三個aaa,兩者的最大匹配長度為3。

另有字串abc1abc1,nextarr[7]的值也是3,因為abc1abc的字首子串abc和字尾子串abc相匹配。

知道了nextarr的含義,那麼這個陣列要如何得到?

因為match[0]前邊沒有其他字元,所以規定nextarr[0]=-1。另外對於match[1]來說前邊只有乙個match[0],所以規定nextarr[1]=0。所以重點放在對match[i] (i>1)時,nextarr的求解。

我們是依次求出nextarr[i],因此我們在求nextarr[i]的時候,0、1、……i-1的情況都已經考慮過了。nextarr[i]的結果也確實需要前邊的結果的幫助來求得。

如圖,這是match陣列的其中一部分,我們想要知道nextarr[i]的值,nextarr[i-1]已經事先得到了。x和y分別為在求nextarr[i-1]時的字首子串和字尾子串。這時候就要分情況考慮:

如果字元b和c相等,那麼字首子串就為x加上字元c,字尾子串就是y加上字元b,因此nextarr[i]=nextarr[i-1]+1。

如果字元b和c不相等,那又要再分情況考慮:

假設字元c是第cn個字元,即match[cn],那麼nextarr[cn]就是其最長字首和字尾匹配長度,字首和字尾分別對應m和n,由於x和y是相匹配的,所以在y中有相應的n』與x中的n相匹配。然後就是看字元b和d是否相等。

如果相等,那麼可以類似於之前的處理,字首子串是m加上字元d,字尾子串是n』加上字元b,因此nextarr[i]=nextarr[cn]+1。

如果b和d不相等,那麼就要調到字元d,對m進行分割,和前一張圖是類似的處理。一致這樣處理,都會有字元和b進行比較,只要有相等的情況,那麼nextarr[i]就能確定。如果始終沒有與b相等的,會最終來到match[0]這個位置,所以就不存在字首和字尾匹配的情況,那麼nextarr[i]=0。

public int getnextarray(char ms) ;

}int next = new int[ms.length];

next[0] = -1;

next[1] = 0;

intpos = 2;

int cn = 0;

while (pos

< next.length) else

if (cn > 0) else

}return

next;

}

有了nextarr陣列,那麼如何讓匹配過程得到加速?

假設從str[i]開始匹配,匹配到j位置的字元發現與match中的字元不一致,也就是說str[i]到str[j-1],與match[0]到match[j-i-1]一一對應。直到str[j]與match[j-i]不匹配了。

如上圖,我們已經有了nextarr陣列,nextarr[j-i]的值表示match[0]到match[j-i-1]這一段字串字首與字尾的最長匹配。這個字首和字尾分別為圖中的a和b。

我們下一次的匹配不需要按照傳統的方法,從str[i+1]開始,而是依然從str[j]繼續向後,讓match[k]滑動到了str[j]的位置上。也就是說str中可以一直向後滑動進行匹配,直到在某一位置把match完全匹配完。過程就如下圖。

為什麼可以這樣做?

一開始是匹配的,str中的a字元和match中的b字元不匹配,所以c和b區域是一樣的。a和b分別為字首和字尾,所以a和b也是一樣的,所以a和c是一樣的。所以將字元c滑動到和a一樣的位置繼續接下來的匹配,這就相當於從str的c區域的第乙個字元重新開始了匹配(c的第乙個字元和match[0]相同)。

不過從str[i]到c區域的第乙個字元跨度有點大,但是不用擔心。

如果要從str[i]和c的第乙個字元之間的某個位置進行下一輪的匹配的話,說明要有一對字首和字尾的長度d和e是大於a和b的,但是a和b本身就是最長的匹配了,前後矛盾,所以之前的處理是沒問題的。

str的位置是不會退回的,滑動的最大長度就是str的長度n,所以時間複雜度為o(n)。匹配的**如下:

public int getindexof(string s, string m) 

char ss = s.tochararray();

char ms = m.tochararray();

int si = 0;

int mi = 0;

int next = getnextarray(ms);

while (si < ss.length && mi < ms.length) else

if (next[mi] == -1) else

}return mi == ms.length ? si - mi : -1;

}

Java實現KMP演算法

對於查詢字元子串在父字串中出現的位置,我們可以使用kmp演算法。kmp演算法的實現原理是使子串向右滑盡可能的遠,這就涉及到求滑動距離的陣列next.next陣列中每個元素求解的公式是 private static void kmpnext int next,string str 這裡需要附加說明的是...

kmp演算法 java實現

通常我們想在乙個字串中匹配乙個子字串,會遍歷字串,對於每乙個字元,都遍歷子字串進行匹配,這樣時間複雜度為o nm 使用kmp演算法只需先進行乙個o m 的預處理 生成next陣列 就能將搜尋的時間複雜度降低至o n m 下面講一講kmp演算法的實現原理。對於abcdeabd.和abcdeabc,當匹...

KMP 演算法 java實現

在網上找到的 執行出來的模式值和資料裡根據定義計算出來的值不一樣,沒有找到合適的,於是手寫了乙個。模式值定義 1 next 0 1 意義 任何串的第乙個字元的模式值規定為 1。2 next j 1 意義 模式串t中下標為j的字元,如果與首字元相同,且j的前面的1 k個字元與開頭的1 k個字元不等 或...