1. 程式人生 > >字串匹配演算法之KMP總結

字串匹配演算法之KMP總結

字串匹配有很多方法,比如暴力,雜湊等等,還有一種廣為人知的演算法 K M P ---KMP

一.問題引入

需要一種演算法,能夠線上性的時間內判斷 a [ 1... N ] a[1...N] 是否是字串 b

[ 1... M ] b[1...M] 的字串,並要求返回字串a在b中匹配的所有位置

思考暴力。

列舉i從 1 >

m 1->m 表示 b b 匹配的左端點,然後 O ( n ) O(n) 的判斷 b [ i . . . i + n 1 ] b[i...i+n-1] 是否與 a [ 1... n ] a[1...n] 匹配。

我們很容易發現這樣的方法在極端資料下跑的很慢,比如:

aaaaaaaaaaaaaaaaaab
aaaaab

每次要匹配到a最後一個位置才發現不相等。時間複雜度 O ( n m ) O(nm)

當然此問題亦可以通過雜湊解決,筆者在此不多贅述

接下來我們就講神奇 K M P KMP 演算法

二.演算法概述

K M P KMP 演算法,又稱模式匹配演算法,是一種能夠高效,準確的處理字串匹配的演算法

KMP演算法基本分為兩部:

1.首先是對 A A 陣列(模式串)進行自我匹配。

建立一個 n e x t next 陣列, n e x t [ i ] next[i] 表示以 i i 結尾的非字首字串與A的字首能夠匹配的最大長度。

其中“以 i i 結尾的非字首字串”通俗的說就是非字首的字尾,比如aab的非字首的字尾就是 { b } , { a b } \{b\},\{ab\}

n e x t [ i ] = max { j } , j < i next[i]=\max\{j\}, j<i A [ i j + 1... i ] = A [ 1... j ] A[i-j+1...i]=A[1...j]

舉個例子:

A A 串為 " a b a b a b a a c " "abababaac" ,A陣列的next[7]應該為5,推導過程如下:

發現有三個可行的j滿足 A [ i j + 1... i ] = A [ 1... j ] A[i-j+1...i]=A[1...j]

A [ 7...7 ] = { a } A[7...7]=\{a\} A [ 1...1 ] = { a } A[1...1]=\{a\} 匹配;
A [ 5...7 ] = { a b a } A[5...7]=\{aba\} A [ 1...3 ] = { a } A[1...3]=\{a\} 匹配;
A [ 3...7 ] = { a b a b a } A[3...7]=\{ababa\} A [ 1..5 ] = { a b a b a } A[1..5]=\{ababa\} 匹配;

其中 j j 最大的是第 3 3 個為 5 5

如何更快的計算 n e x t next 陣列?

不妨設 n e x t [ 1...6 ] next[1...6] 都已求出,通過上述過程知 n e x t [ 6 ] = 4 next[6]=4

A [ 7 ] = A [ 5 ] , n e x t [ 7 ] = n e x t [ 6 ] + 1 = 5 ∵A[7]=A[5],∴next[7]=next[6]+1=5

接下來考慮next[8]

發現 A [ 8 ] = { a } A[8]=\{a\} A [ 6 ] = { b } A[6]=\{b\} ,所以 n e x t [ 8 ] next[8] 不等於 n e x t [ 7 ] + 1 next[7]+1

那麼只好將匹配長度 j j 縮短

根據上面的結論我們知道 j j 好可以等於 3 3 5 5 ,嘗試延伸到 A [ 8 ] A[8]

但是我們發現 A [ 8 ] A[8] A [ 4 ] A[4] A [ 2 ] A[2] 都不匹配,於是只能從頭匹配, n e x t [ 8 ] = n e x t [ 1 ] + 1 = 1 next[8]=next[1]+1=1

那我們怎麼讓程式知道當我們發現 A [ 8 ] ! = A [ 6 ] A[8]!=A[6] 時該去匹配 A [ 4 ] A[4] A [ 2 ] A[2] 呢?

n e x t [ 7 ] = 5 next[7]=5 說明從 7 7 往前 5 5 個字元是與 A [ 1...5 ] A[1...5] 匹配的。那我們下一步要尋找的也就是 5 5 之前的 j j 個字元與 A [ 1... j ] A[1...j] 相匹配,那麼 7 7 往前 j j 個字元是與 A [ 1... j ] A[1...j] 匹配的。這個 j j 的答案就是 n e x t [ 5 ] next[5] ,其實就是 n e x t [ n e x t [ 7 ] ] next[next[7]] <