很早以前就了解到有kmp演算法的存在,當時就是知道其可以高效的匹配字串,但是沒敢細看(其實看了一眼,又嚇得趕緊退出來。。),最近突然又看到這個演算法,就想著學習一下,鑑於自己理解力不太夠,花了好久才明白個大概,因此防止過兩天就忘,在這裡做個筆記。
字串字首與字尾
如下圖所示:
kmp演算法思想
在匹配的過程中,如文字串s匹配到i,模式串匹配p到位置j時,兩者不匹配,即s[i]!=p[j]時,保持i不變,j移動到next[j]=k位置處,k等於p[j]之前子串的最大前字尾的長度。相當於將模式串p向後移動j-next[j]個位置。
與暴力破解法相比,其文字串s指標不會回溯。
暴力匹配**(python):
def
baoli
(s,t):
s_len=len(s) #目標字串
t_len=len(t) #匹配字串
i=0j=0
while iand j if s[i]==t[j]:
i+=1
j+=1
else:
j=0i=i-j+1
if j==t_len:
return i-j
else:
return -1
if __name__=='__main__':
s='sdfwowjeofijowejifeowji'
t='feo'
print(baoli(s,t))
kmp演算法複雜度
kmp演算法是指在乙個文字串s內查詢乙個模式串p 的出現位置,假設s的長度為n,p的長度為m,傳統的暴力匹配法時間複雜度為o(m*n),而kmp演算法由兩個部分構成:
因此,kmp演算法的時間複雜度為o(m+n)
kmp演算法過程
前面提到,kmp演算法由兩個過程組成,首先第二個過程比較好理解。
def
kmp(s,t):
s_len=len(s) #目標字串
t_len=len(t) #匹配字串
i=0j=0
next_array=get_nextarray(t)
while iand jif j==-1
or s[i]==t[j]: #j==-1存在於開頭第乙個字元就沒匹配上,此時next[j]=-1,因此i+=1後目標字串向右移一位等於0,
j+=1
#表示匹配字串繼續從第乙個開始匹配
i+=1
else:
j=next_array[j]
if j==t_len:
return i-j
else:
return -1
kmp演算法核心在於第一部分:求next陣列。它是通過遞推的方式來解決:已知next [0, …, j],如何求出next [j + 1]??分為兩種情況:
關鍵:為什麼遞迴k=next[k],當p[k]=p[j]時就可以得到next[j+1]=k+1
我在這個地方糾結了很久,不知其所以然心裡總是不得勁。。相信很多人跟我一樣,因此就自己舉例子,通過例子來理解會更清楚。其實這就是next陣列的特性,這裡的遞迴:k = next[k]與第一部分kmp演算法匹配中的:j=next_array[j]原理是一樣的,前者是與模式串p的p[j]比較來決定是否遞迴,後者是與文字串s中的s[i]對比
如上圖所示,求取next[j+1]時,已知next[:j],且next[j]=4=k,此時根據遞推式,因為p[j]=』c』,p[k]=p[4]=』a』,因此令k=next[k]=1,此時p[k]=p[1]=』c』=p[j],因此next[j+1]=k+1=2。
通過上述例子可以看到,當p[k]=p[1]=』c』與p[j]相等時候,其前面的k個字元必然等於p[4]=』a』前面的k個字元,同樣等於p[j]前面k個字元,因此得到next[j+1]=k+1
第二部分j=next_array[j]原理也是一樣,只是它不是跟p[j]比較,而是與文字串s[i]比較。都是在最長前字尾子串中尋找最長子串,直到匹配為止。
next陣列求取過程(python):
def
get_nextarray
(t):
next_array=[-1]
t_len=len(t)
k=-1
#因為採用的是從前往後遞推的方法,因此需要知道初始狀態:j=0時,next[0]=k=-1
j=0while j1: #之所以是len-1:next[0]是固定值為-1
if k==-1
or t[j]==t[k]:
k+=1
j+=1
else:
k=next_array[k]
return next_array
然而上述next陣列存在一定的問題,以下圖為例,若在i=5時匹配失敗,按照3.2的**,此時應該把i=1處的字元拿過來繼續比較,但是這兩個位置的字元是一樣的,都是b,既然一樣,拿過來比較不就是無用功了麼?之所以會這樣是因為kmp不夠完美。因此需要對**進行一定的修改,如下所示:
next陣列(優化後)求取過程(python):
def
get_nextarray_inp
(t):
#改進的next陣列,當k=next[j],且p[j]=p[k]時,即next[j]=next[next[j]]=next[k]
next_array=[-1]
t_len=len(t)
k=-1
j=0while j1:
if k==-1
or t[j]==t[k]:
k+=1
j+=1
if t[j]==t[k]:
else:
else:
k=next_array[k]
return next_array
kmp演算法優化版與未優化版在某些情況下區別很大,如下所示:
kmp演算法(未優化版): next陣列表示最長的相同前字尾的長度,我們不僅可以利用next來解決模式串的匹配問題,也可以用來解決類似字串重複問題等等,這類問題大家可以在各大oj找到,這裡不作過多表述。
kmp演算法(優化版): 根據**很容易知道,優化後的next僅僅表示相同前字尾的長度,但不一定是最長(我個人稱之為「最優相同前字尾」)。此時我們利用優化後的next可以在模式串匹配問題中以更快的速度得到我們的答案(相較於未優化版),但是上述所說的字串重複問題,優化版本則束手無策。
所以,該採用哪個版本,取決於你在現實中遇到的實際問題。
KMP演算法小結
posted on 2011 06 14 byhuangchao 主要看了這裡,感覺講的十分的不錯,總結一下。首先宣告要搜尋的串為s,設長度為n,要匹配的串為m,設長度為m.先考慮暴力的演算法,暴力的演算法是遍歷s的每乙個字元,然後從這個字元開始和m串進行匹配。時間複雜度為o nm 怎麼在此基礎上進...
KMP演算法小結
主要看了這裡,感覺講的十分的不錯,總結一下。首先宣告要搜尋的串為s,設長度為n,要匹配的串為m,設長度為m.先考慮暴力的演算法,暴力的演算法是遍歷s的每乙個字元,然後從這個字元開始和m串進行匹配。時間複雜度為o nm 怎麼在此基礎上進行優化?假設現在從某個位置 設為s 開始和m串進行匹配,如果匹配不...
KMP演算法小結
文字串長度為n,模式串長度為m,匹配過程的時間複雜度為o n 計算next的o m 時間,kmp的整體時間複雜度為o m n 步驟 文字串s bbc abcdab abcdabcdabde 模式串p abcdabd 1.尋找p的 最長公共元素表 前字尾最長序列長度 失配時,模式串向右移動的位數為 已...