KMP演算法

一個人能走的多遠,不在於他在順境時能走多快,而在於在逆境時多久能找到曾經的自己

最長共通前綴後綴

Longest Common Prefix Also Suffix,簡稱LPS。
“abab”的前綴有[“a”,”ab”,”aba”],後綴有[“b”,”ab”,”bab”],所以LPS是”ab”。

Partial Match Table

部分匹配表格,簡稱PMT。在KMP使用時又稱Failure Function,因為只有在匹配失敗時用到。
計算p的每個子字串的LPS長度。

i 0 1 2 3 4
p[i] a b a b c
substring a ab aba abab ababc
LPS 0 0 1 2 0

例如i=4,ababc和ababb匹配失敗時,至少”abab”部分是匹配成功的,而”abab”有LPS=2,代表可以重複利用最後兩個字元”ab”。i回退到PMT[3]=2的位置,也就是p[2]的”a”繼續匹配,省略第一次出現的”ab”比對。

Next Table

只有當第i個字元匹配失敗時才會用到PMT[i-1],所以PMT最後一格永遠不會用到。而且要特別處理i=0匹配失敗時的特例,因為PMT[-1]不存在。
為了方便計算,把PMT整個往右移一格,table[0]設為-1。之後在i失敗時直接回退到table[i]。只有在目標字串第一個字元就匹配失敗時,才會讓j變成-1,此時兩方的索引同時+1。

i 0 1 2 3 4
p[i] a b a b c
table -1 0 0 1 2

KMP字串比對

來源字串s,目標字串p,長度分別為M, N。
先以p建立next table。
i, j分別為s, p的指針,初始值0,每次比對s[i]和p[j],是否相同:

  • 若s[i]=s[j],兩者同時+1
  • 若j=-1,一樣兩者+1
  • 否則將j退回至table[j]的位置

重複至其中一個字串全部讀取完為止。若j=N則表示成功找到目標字串p,其起始索引為i-j;否則匹配失敗,回傳-1。

最後整合練習

s=”abaababaca”
p=”abac”

先以p建立next table:

table[0]=-1
i=1, prefix=”a”, suffix=”b”, LPS=0, table[1]=0
i=2, prefix=”ab”, suffix=”ac”, LPS=0, table[2]=0
i=3, prefix=”aba”, suffix=”bac”, LPS=1, table[3]=1
table=[-1,0,0,1]

利用table進行比對:

i=j=0
s[0]=p[0], i++, j++ 已找到”a”
s[1]=p[1], i++, j++
已找到”ab”
s[2]=p[2], i++, j++
已找到”aba”
s[3]!=p[3], j=table[3]=1
已找到”a”
s[3]!=p[1], j=table[1]=0
已找到””
s[3]=p[0], i++, j++
已找到”a”
s[4]=p[1], i++, j++
已找到”ab”
s[5]=p[2], i++, j++
已找到”abc”
s[6]!=p[3], j=table[3]=1
已找到”a”
s[6]=p[1], i++, j++
已找到”ab”
s[7]=p[2], i++, j++
已找到”aba”
s[8]=p[3], i++, j++
已找到”abac” 比對結束
p的起始位置為j-i=5

心得

字串比對時,基本上j的位置等價於已經成功找到的字元數,因此j和目標字串長度相等也代表成功找到。

next table、partial match table、prefix function和failure function在KMP文章出現時,指的都是同個東西,只是有些版本會將其右移一格方便計算。

建表的時候i=1, j=0
KMP的時候i=0, j=0

KMP只有三種情況:

  1. 成功,兩個都右移
  2. 失敗,j還有辦法左移就左移
  3. 失敗,j已經在起點了,只好把i右移

2023-9-14更新:
好像其實原版的PMT比較好用,用得人也多,盡量用原版的就好。

程式碼

原版的PMT
使用右移next table的KMP

參考資料

https://yeefun.github.io/kmp-algorithm-for-beginners/
https://www.zhihu.com/question/21923021/answer/281346746
https://zhuanlan.zhihu.com/p/36190375
https://cmps-people.ok.ubc.ca/ylucet/DS/KnuthMorrisPratt.html
https://oi-wiki.org/string/kmp/
https://www.zhihu.com/question/21923021/answer/37475572

Tags:

Updated: