1. 程式人生 > 程式設計 >詳解KMP演算法以及python如何實現

詳解KMP演算法以及python如何實現

演算法思路

Knuth-Morris-Pratt(KMP)演算法是解決字串匹配問題的經典演算法,下面通過一個例子來演示一下:

給定字串"BBC ABCDAB ABCDABCDABDE",檢查裡面是否包含另一個字串"ABCDABD"。

1.從頭開始依次匹配字元,如果不匹配就跳到下一個字元

詳解KMP演算法以及python如何實現

詳解KMP演算法以及python如何實現

2.直到發現匹配字元,然後經過一個內迴圈嚴查字串是否匹配

詳解KMP演算法以及python如何實現

3.發現最後一個D不匹配,下面就該思考應該把字串向右移動多少個位置呢?傳統做法可能是移動一格,KMP演算法就創新在這裡。KMP演算法通過查詢一個Partial Match Table(表記憶體有字串資訊),然後計算出需要移動的步數,這個表後面會介紹怎麼來的。

詳解KMP演算法以及python如何實現

這裡我們看到D前面是B,查表得到第二個B對應的是2,所以 移動數 = 已匹配字元數 - 查表所得數 也就是 6 - 2 = 4, 需要向右移動四格。

詳解KMP演算法以及python如何實現

下面也是重複這個步驟

詳解KMP演算法以及python如何實現

直到發現匹配或者字元長度超出(未發現匹配)。

Partial Match Table

那麼這個查詢的表是怎麼來的呢?仍然以"ABCDABD"為例

詳解KMP演算法以及python如何實現

- "A"的字首和字尾都為空集,共有元素的長度為0;

- "AB"的字首為[A],字尾為[B],共有元素的長度為0;

- "ABC"的字首為[A,AB],字尾為[BC,C],共有元素的長度0;

- "ABCD"的字首為[A,AB,ABC],字尾為[BCD,CD,D],共有元素的長度為0;

- "ABCDA"的字首為[A,ABC,ABCD],字尾為[BCDA,CDA,DA,A],共有元素為"A",長度為1;

- "ABCDAB"的字首為[A,ABCD,ABCDA],字尾為[BCDAB,CDAB,DAB,B],共有元素為"AB",長度為2;

- "ABCDABD"的字首為[A,ABCDA,ABCDAB],字尾為[BCDABD,CDABD,DABD,ABD,BD,D],共有元素的長度為0。

python實現

def partial_table(p):
  '''''partial_table("ABCDABD") -> [0,1,2,0]'''
  prefix = set()
  res = [0]
  for i in range(1,len(p)):
    prefix.add(p[:i])
    postfix = {p[j:i + 1] for j in range(1,i + 1)}
    #print(p[:i+1],prefix,postfix,prefix & postfix or {''})
    res.append(len((prefix & postfix or {''}).pop()))
  return res

def kmp_match(s,p):
  m = len(s);
  n = len(p)
  cur = 0 # 起始指標cur
  table = partial_table(p)
  while cur <= m - n:   #只去匹配前m-n個
    for i in range(n):
      if s[i + cur] != p[i]:
        cur += max(i - table[i - 1],1) # 有了部分匹配表,我們不只是單純的1位1位往右移,可以一次移動多位
        break
    else:    
      return True # loop從 break 中退出時,else 部分不執行。
  return False

print partial_table1("ABCDABD")
print kmp_match("BBC ABCDAB ABCDABCDABDE","ABCDABD")

以上就是詳解KMP演算法以及python如何實現的詳細內容,更多關於python實現KMP演算法的資料請關注我們其它相關文章!