對KMP演算法的理解

2021-06-05 01:36:43 字數 3422 閱讀 8188

kmp演算法是一種高效的模式匹配演算法,複雜度可以達到o(m+n),而普通模式匹配演算法的複雜度為o(m*n)。

普通模式匹配演算法

從主串的第乙個字元(或者給定的第pos個字元)開始和子串的第乙個字元開始比較,若相等,則繼續比較後面的字元。若不相等,則從主串本次開始比較的字元的下乙個字元開始,與子串的第乙個字元進行比較(即主串需要回退到本次比較開始字元的下一字元,模式串回退到首字元,主串與子串都需要回退)。

匹配成功的標誌:比較到子串的』/0』

匹配失敗的標誌:比較到主串的』/0』,且此時子串的比較位不等於』/0』。

演算法複雜度:o(m*n)

kmp演算法

kmp演算法的改進思路

在普通匹配演算法中子串與模式串都需要回溯,但這些回溯不是必要的。因為當某一位發生失配時,可以根據已匹配的結果進行判斷。該演算法問題可以理解為,當模式串中的第k位與主串的第i位比較時發生不匹配時,需要將模式串向右滑動到**繼續與主串的第i位進行比較?避免了不必要的主串回溯,減少了模式串回溯的位數,從而使演算法複雜度提公升到o(m+n)。

kmp演算法的實現思路

從主串的第乙個字元(或者給定的第pos個字元)開始和子串的第乙個字元開始比較,若相等,則繼續比較後面的字元。若不相等,則將模式串右移至合適的位置,找出模式串中合適的第k位與主串中發生不等的位進行對齊比較。演算法繼續。

模式串與主串對齊繼續比較的第k位必須滿足其前k-1位與主串發生失配位置的前k-1位匹配,且該k-1位字串必須是最長的字串(即不存在k』>k,使模式串中的前k』-1位與主串發生失配位置的前k』-1位匹配,這是為了保證不漏過可以匹配的串)。

該演算法中的主程式同普通的匹配演算法類似,區別在於當發生不匹配時,主串指標不需要回退(不動),將模式串右移到合適的位置繼續進行比較。當模式串移動到第一位(下標為0)仍然不等時,主串指標右移一位。該演算法的關鍵是模式串next的取得。

匹配成功的標誌:比較到子串的』/0』

匹配失敗的標誌:比較到主串的』/0』,且此時子串的比較位不等於』/0』。

next陣列的獲得

next陣列記錄了當模式串第j位發生失配時,模式串需要移動到第k位,使第k位與主串發生失配的位對齊繼續比較(next[j]=k)。next陣列使用遞推實現,假設next[j]=k已經獲得,則從next[j]開始推next[j+1]。具體演算法如下。

假設next[j]=k(第j位及之前的next值已經求得,k而同時由於:p[j-k+1]p[j-k+2]…p[j-1]=s[i-k+1]s[i-k+2]…s[i-1]

所以:p[1]p[2]…p[k-1]= p[j-k+1]p[j-k+2]…p[j-1]

情況1:

若p[k]=p[j],即p[next[j]]=p[j],那麼:p[1]p[2]…p[k-1]p[k]= p[j-k+1]p[j-k+2]…p[j-1]p[j],則根據定義:next[j+1]=k+1=next[j]+1

情況2:

若p[k]!=p[j],即p[1]p[2]…p[k-1]p[k]!= p[j-k+1]p[j-k+2]…p[j-1]p[j],即p[k]與p[j]發生不等。把p[k]為模式串,即模式串第k位發生失配,根據kmp演算法的基本思路,當模式串第k位發生失配時,模式串應移動到第next[k]=k』位與主串進行比較。此時:p[1]p[2]…p[k』-1]= p[j-k』+1]p[j-k』+2]…p[j-1]。然後再比較p[k』]與p[j],若相等,同情況1:next[j+1]=k』+1= next[k]+1;若不相等,返回到情況2的頭進行比較。(兩種判斷均可以歸入情況1或者情況2,所以可以進行迴圈)

情況3:

若next[k]=-1,即發生失配元素的前乙個元素與第乙個元素a[0]仍然不等,該應使該失配元素直接與第乙個元素比較,next[j+1]=0。(該情況可與第一種情況合併(因為next[0]=-1))

簡單得說:

從next[j]=k開始比較,首先將k與自身對齊比較(找可以與j對齊比較的最長子串),如果相等,則p[k]=p[j],滿足情況1。若不相同,即模式串第k位發生失配,移動到k'=next[k]位繼續進行比較。返回情況1或者2。如果next[k]=-1(僅第1位的next[0]的值為-1),說明j+1的前一位j與第一位比較仍然不等,那應該讓第j+1位直接與第1位(下標為0)進行比較。

next[k+1]的計算依賴於next[k],next[k+1]僅有一種方式獲得,即第k+1字元的前面k個字元完全匹配(或者前一元素與第乙個元素仍然不匹配)。而next[k]是已經計算的,即next[k]可以保證第k字元的前面k-1個字元(除了第k個字元)完全匹配,而且該字串是最長字串。因為只需比較第k個字元是否匹配。匹配成功即情況一,匹配失敗則繼續尋找next[next[k]]。

程式設計時,每次進行計算next[j+1]時,必須保證j與k呈對應關係,即每新一次計算前,next[j]=k(next[j+1]=k+1,因而也可以寫成next[++j]=++k)。

kmp演算法的再改進

當第k+1位發生失配時,根據原演算法,可以找到乙個子串,與之匹配。此時,next[j+1]=k+1

但若 t[j+1]= t[next[j+1]]=t[k+1],則當t[j+1]發生失配時,程式會使回到第next[j+1](=k+1)位,與t[j+1]所對應的主串字元進行比較。但由於主串字元已經不等於t[j+1],因而也必然不等於t[next[j+1]],因而此次比較必然失敗。

改進:當t[j+1]= t[next[j+1]]=t[k+1]時,next[j+1]= next[next[j+1]]=next[k+1],從而避免不必要的重複比較。

附:普通模式匹配演算法**:

int mystrstr(char* s, char* t, int pos)

//比較到子串的』/0』,匹配成功

if (s[i]==t[j]) else

}return 0; 比較到主串的』/0』,且此時子串的比較位不等於』/0』,匹配失敗

}kmp主程式**:

int mystrstr(char* s, char* t, int pos, const int* next)

// 當頭元素仍然不匹配時,j=-1,此時j指標清0指向模式串的首元素, i指標指下主串的下一元素

if (t[j]=='/0')

// 當模式串指標所指元素為'/0'時,匹配完成,返回位置

if (s[i]==t[j]) else

}return 0;

}生成next陣列**

//該程式用next[i]來推測next[i+1]

//每次比較開始時next[i]=k,每次比較開始前i與k成對應關係

void getnext (char* t,int* next)

else }}

生成kmp改進演算法的next陣列**

void getnext (char* t,int* next)

//就多了這段,當t[j+1]與t[next[j+1]]相等時,使next[j+1]=next[next[j+1]]

j++;

k++;

} else

}                

}

對KMP演算法的理解

kmp演算法是一種高效的模式匹配演算法,複雜度可以達到o m n 而普通模式匹配演算法的複雜度為o m n 普通模式匹配演算法 從主串的第乙個字元 或者給定的第pos個字元 開始和子串的第乙個字元開始比較,若相等,則繼續比較後面的字元。若不相等,則從主串本次開始比較的字元的下乙個字元開始,與子串的第...

對KMP演算法的理解

kmp演算法是一種高效的模式匹配演算法,複雜度可以達到o m n 而普通模式匹配演算法的複雜度為o m n 普通模式匹配演算法 從主串的第乙個字元 或者給定的第pos個字元 開始和子串的第乙個字元開始比較,若相等,則繼續比較後面的字元。若不相等,則從主串本次開始比較的字元的下乙個字元開始,與子串的第...

對kmp演算法的理解

設有兩個串s和t,在s串中找到乙個與t串相等的子串。我們通常把s稱為目標串,t稱為模式串。在講kmp演算法之前,先回憶一下簡單的暴力匹配演算法。其大概的思路為先從目標串的第乙個字元開始和模式串的第乙個字元相比較,若相等,則比較後續字元,否則從目標串的第二個字元開始重新比較。依此類推,如果匹配成功,輸...