BM 與 KMP 演算法#

KMP 演算法#

核心概念#

當匹配失敗時,利用已匹配的「好前綴」資訊,將模式串多滑動幾位。

主串:    A B A B A B C
模式串:  A B A B C
               ↑ 失敗

傳統:後移 1 位
KMP:後移 2 位(利用 "ABAB" 的前綴 "AB" = 後綴 "AB")

關鍵:next 陣列(失效函式)#

next[i] = 子串 pattern[0..i]最長相等前後綴長度 - 1

模式串:A B A B C
next:  -1 -1 0 1 -1

解釋:
- "A": 無前後綴 → -1
- "AB": 無相等前後綴 → -1
- "ABA": "A" = "A" → 0
- "ABAB": "AB" = "AB" → 1
- "ABABC": 無相等前後綴 → -1

next 陣列計算#

next 陣列程式碼
def get_next(pattern):
    m = len(pattern)
    next_arr = [-1] * m
    k = -1

    for i in range(1, m):
        # 當 pattern[k+1] != pattern[i] 時,回退
        while k != -1 and pattern[k + 1] != pattern[i]:
            k = next_arr[k]

        if pattern[k + 1] == pattern[i]:
            k += 1

        next_arr[i] = k

    return next_arr

理解技巧:這是一個自我匹配的過程,用模式串匹配自己來找重複結構。

KMP 主函式#

def kmp_search(text, pattern):
    n, m = len(text), len(pattern)
    next_arr = get_next(pattern)

    j = 0
    for i in range(n):
        while j > 0 and text[i] != pattern[j]:
            j = next_arr[j - 1] + 1

        if text[i] == pattern[j]:
            j += 1

        if j == m:
            return i - m + 1

    return -1

複雜度#

  • 預處理:O(m)
  • 匹配:O(n)
  • 總計:O(n + m)

BM 演算法#

核心規則#

  1. 壞字元規則:從後往前匹配,遇到不匹配字元時,將模式串滑動到該字元對齊
  2. 好後綴規則:利用已匹配的後綴,找模式串中的相同子串對齊

BM 的特點

  • 從後往前匹配
  • 實務中通常比 KMP 更快
  • 最佳情況 O(n/m)

為什麼 BM 更快?#

主串:    ... X Y Z A B C D E F ...
模式串:        A B C D E F

從後往前:F 匹配,E 匹配,D 匹配,C 匹配,B 匹配,A 匹配 → 成功

若 X 與模式串不匹配且 X 不在模式串中:
→ 直接跳過整個模式串長度!

演算法對比#

特性KMPBM
匹配方向從前往後從後往前
時間複雜度O(n)O(n/m) ~ O(n)
預處理next 陣列壞字元表 + 好後綴表
實現難度中等較複雜
實務效能更好(統計意義上)

應用場景#

場景推薦演算法
文字編輯器搜尋BM(快)
實現簡單優先KMP
字元集小、模式短RK
多模式匹配AC 自動機

面試建議

  • 理解 KMP 的 next 陣列原理
  • 能手寫 KMP 程式碼
  • 了解 BM 的思想,但不需要背誦細節
最難理解的部分:k = next[k]

pattern[k+1] != pattern[i] 時,需要找次長的相等前後綴。

假設 next[i-1] = k-1
即 pattern[0..k-1] = pattern[i-k..i-1](最長相等前後綴)

若 pattern[k] != pattern[i]:
- 需要找 pattern[0..k-1] 的最長相等前後綴
- 這就是 next[k-1]!
- 所以 k = next[k]

本質上是利用已經計算好的結果,避免重複計算。