字符串匹配在工作中我們經常會用到,同時也是各大公司面試中的常考題目。字符串匹配的算法有很多,所以需要深入學習的東西也有很多。我們接下來會有一系列的文章去把字符串匹配算法盡量說明白。
今天我們主要聊一下單模式串匹配算法---即一個串去跟另外一個串去比較。在開始之前,為了后續方便講解,我們先明確兩個定義,即主串和模式串。如果我們要在長度為n的字符串A中查找長度為m字符串B,那么A就是主串,B就是模式串,其中n>m。我們先從最簡單的BF算法說起。
BF算法
BF算法也叫做暴力匹配算法,也是最直接、簡單的算法。所謂的暴力匹配算法,就是固定主串,然后模式串一步步向前移動,一位一位的對比,直到在主串中找到相匹配的子串。如下圖所示。

def bf(a, b):
n = len(a)
m = len(b)
if n <= m:
return 0 if b == a else -1
for i in range(n-m+1):
for j in range(m):
if a[i+j] == b[j]:
if j == m-1:
return i
else:
continue
else:
break
return -1
if __name__ == '__main__':
a = 'cbdac'
b = 'ac'
start=bf(a, b)
print('result:', start)
#####輸出####
result: 3
從上面的代碼我們可以看出,這種算法的最壞情況時間復雜度是 O(n*m)。
RK算法
RK算法的全稱叫Rabin-Karp算法。是由它的兩位發明者 Rabin 和 Karp 的名字來命名的。RK算法的思想就是通過比較2個字符串的Hash值來判斷字符串是不是相等的。我們在BF算法中,如果主串的長度是n,模式串的長度是m,我們需要暴力的比較n-m+1個子串和模式串,來找出主串和模式串相匹配的子串。在子串和模式串比較的時候,需要一位一位的對比,所以BF算法的時間復雜度較高,是O(N*M)。而RK算法的思路是:通過哈希算法把n-m+1個子串分別求hash值,然后再和模式串的hash值比較大小。如果某個子串的哈希值和模式串相等。那就說明對應的子串和模式串相匹配了(我們先忽略哈希沖突的情況)。因為hash值的比較是非常快速的,所以子串和模式串比較的效率就提高了。如下圖所示。

不過,通過哈希算法計算哈希值的時候,是需要遍歷子串中的每個字符。雖然子串和模式串比較的效率提高了,但是算法的整體效率卻沒有提高,那如何提高哈希算法計算子串哈希值的效率呢?這就需要設計一個更高效的哈希算法。我們假設要匹配的字符串的字符集中只包含K個字符,我們可以用一個K進制數來表示一個子串,這個K進制數轉化成十進制數,作為子串的哈希值。我們舉個例子來說明一下。假如我們要處理的字符串只含有a~z這26個小寫字母,我們把a~z映射到0~25這26個數字中,a表示0,b表示1,依次類推。所以字符串"cdb"的哈希值為:
Hash("cdb")=c*26*26+d*26+b=2*26*26+3*26+1=1431
這種哈希算法有一個特點,就是在主串中,相鄰兩個子串S[i-1]和S[i](其中i表示子串在主串中的起始位置),對應的哈希值的計算公式是有交集的,也就是說我們可以根據S[i-1]的哈希值,很快的計算出S[i]的哈希值。我們來用公式表示一下。

我們可以把26^0、26^1、26^2......26^(m-1)先計算出來,並且存儲在一個長度為m的數組中,公式中的“次方”就對應數組的下標。當我們需要計算26的x次方的時候,就可以從數組的下標為x的位置取值,直接使用,這樣我們就省去了計算的時間。

RK算法中主要包括計算子串的哈希值和模式串哈希值與子串哈希值之間的比較。由於我們可以通過設計特殊的哈希算法,只需要掃描一遍主串就能計算出所有子串的哈希值了,所以計算子串哈希值的時間復雜度為O(n)。
模式串哈希值與每個子串哈希值之間的比較的時間復雜度是O(1),總共需要比較 n-m+1 個子串的哈希值,所以,這部分的時間復雜度也是 O(n)。所以,RK 算法整體的時間復雜度就是 O(n)。
還有一個問題需要注意,如果我們通過上面計算哈希值的方法計算的哈希值太大,超過了計算機表示的范圍,那我們該如何解決呢?剛剛我們設計的哈希算法是沒有散列沖突的,也就是說,一個字符串與一個二十六進制數一一對應,不同的字符串的哈希值肯定不一樣。實際上,我們為了能將哈希值落在整型數據范圍內,可以犧牲一下,允許哈希沖突。這個時候哈希算法該如何設計呢?其實很簡單,我們可以對一個大的素數取模。這樣的話就會帶來hash沖突的問題,如果有哈希沖突的話,我們在發現一個子串的哈希值跟模式串的哈希值相等的時候,還需要去對比一下子串和模式串本身。
好了,我們今天就先聊到這里,下篇文章我們再來聊一下更高效的字符串匹配算法BM算法和KMP算法。
更多硬核知識,請關注公眾號。

