KMP演算法學習的一點個人理解

2021-08-31 07:41:08 字數 3058 閱讀 5957

假設有兩個字串s, r,求r在s中出現的位置,效率最高的演算法似乎就是kmp了。

演算法的第一步是生成r的next陣列,next[i],其中0<=i減去1。換一種理解,next[i]即為子串r[:i+1]中最大的相同前字尾中最大字首的末尾的索引。

用乙個例子說明,若r = "ababc"

next[0],即子串"a"的最大的相同前字尾長度顯然是0.因為"a"的長度為1,是不可能有字首或字尾的,故值為-1

next[1],即子串「ab」,同樣,也不存在相同的前字尾,值為-1

next[2],即子串"aba",此時,最大的相同前字尾顯然就是"a",所以next[i]為子串「aba」中最大的相同前字尾中最大字首「a」的末尾的索引,即0.

next[3],即子串"abab",此時,滿足條件最大字首為"ab",那麼next[3]的值為1

next[4],即子串"ababc",此時顯然無滿足條件的相同前字尾,值為-1

所以,"ababc"的next陣列為:[-1, -1, 0, 1, -1]

那麼,如何用程式去計算出這個next陣列呢?

假設我們已經有了0...i的next值,那麼,現在要求next[i+1]的值,

我們先獲取next[i]的值,這個值為子串r[:i+1]的最大的相同前字尾中最大字首的末尾的索引,記為k.

那麼r[:k+1] 就等於 r[i-k:i+1],

(*)此時我們考察r[i+1]是否等於r[k+1]:

如果兩者相等,那麼對於子串r[:i+2]來說,其最大的相同的前字尾即為r[:k+2],故而next[i+1]的值就是k+1

如果兩者不等,這裡面最有意思的是,我們取k = next[k],然後繼續比較r[i+1]是否等於r[k+1], 回到(*)處迴圈了起來。

結合上述例子,看下i=2的時候,此時next[2] = 0, 現在求next[3]的值,

那麼k = next[2], 即k= 0,此時考察r[i+1]是否等於r[k+1],即r[3]是否等於r[1],這裡兩者都是"b",相等,因此next[3] = k + 1,即1.

對照例項,繼續理解的話,這個演算法理解起來就不難了。

這裡貼出我理解後的python實現:

def gen_next(r):

m = len(r)

if m == 1:

return [-1]

next = [-1 for _ in range(m)]

k = -1

for i in range(1, m):

no_match = false

while r[k +1] != r[i]:

if k == -1:

next[i] = -1

no_match = true

break

k = next[k]

if no_match:

continue

k += 1

next[i] = k

return next

print(gen_next("ababaca"))

其實能夠手擼next出來的,也很容易就能把這個搜尋函式給碼出來。

對於從字串l中搜尋子串r, 假設我們在此場景:

當前搜尋到l的i位置,即目前掃瞄到字元l[i],對於r,已經有(k+1)個字元匹配,當前正在掃瞄r[k+1],並且此時必然有:

r[:k+1]等於l[i-k-1:i]。那麼,此時考察l[i]是否等於r[k+1],

(*)若l[i] == r[k+1]:

說明串進一步匹配,i和k更進一步

若不等:

此時取k = next[k],回到(*)處繼續判斷。

這裡又出現了k = next[k],這應該就是kmp的靈魂所在了。其實光看文字描述感覺很抽象,但是結合實際例子,便很直觀了

這裡畫了張示意圖,其中紅色部分為相同串。

接下來是最後的python實現:

def gen_next(r):

m = len(r)

if m == 1:

return [-1]

next = [-1 for _ in range(m)]

k = -1

for i in range(1, m):

no_match = false

while r[k +1] != r[i]:

if k == -1:

next[i] = -1

no_match = true

break

k = next[k]

if no_match:

continue

k += 1

next[i] = k

return next

def kmp(l, r):

l_len = len(l)

r_len = len(r)

if l_len == 0 or r_len == 0:

return

next = gen_next(r)

k = -1

for i in range(l_len):

no_match = false

while l[i] != r[k + 1]:

if k == -1:

no_match = true

break

k = next[k]

if no_match:

continue

k += 1

if k == r_len - 1:

return i - k

print(kmp("bacbababadababacambabacaddababacasdsd", "ababaca"))

後記:根據演算法去理解其意圖是比較簡單的,但是從無到有創造出kmp演算法的人真她娘的是個天才。

static的一點個人理解

public class xuexi static 個人這麼理解 xuexi myxuexi new xuexi xuexi myxuexi2 new xuexi 程式執行時 當用 xuexi這個類 宣告這個 myxuexi 變數時,就會首先 載入xuexi類 在堆中 為xuexi類開闢一塊空間,在...

關於SVM的一點個人理解

網上關於svm的資料很多,但是我還是想簡單說下自己的理解,希望能為各位初學者做乙個參考 通俗的說,svm的原理就是對於n維資料,尋找乙個n 1維的超平面,將這n維資料區別開來。關於這個超平面,有個學問,如何找到這個超平面呢?就是使得離超平面比較近的點 所謂的支援向量 之間的距離最大,這樣的分類效果才...

關於SG SP, GG GP的一點個人理解

在翻譯tmmi時候,不可繞過的一些名詞就是sg sp,gg gp,具體的翻譯就是特定目標 特定實踐,通用目標 通用實踐。下面把我個人對這些詞的理解寫一下。先說sg sp.sg 乙個過程域通常是由多個子目標組成的。比如說tmmi2裡面的pa1 測試方針和策略 它包含了多個子目標。比如建立測試方針,建立...