已發布·持續改進
Algorithm
字串演算法用於在文字中尋找模式、依前綴檢索單字,核心技術包括 KMP、Rabin-Karp、字典樹與 Z 演算法。
字串演算法研究如何快速、正確地比較與搜尋字元序列。最核心的問題是模式比對,也就是找出短模式在長文字中出現的所有位置;在字典中尋找以某個前綴開頭的所有單字、一次尋找多個模式也屬於這個領域。逐一比較每個位置的樸素方法,在最壞情況下需要與文字長度和模式長度之積成正比的時間。
編輯器搜尋、日誌搜尋、搜尋框自動完成、入侵偵測、檔案同步與基因體分析都建立在字串演算法之上。KMP 透過失敗表重複利用已經做過的比較,保證線性時間;Rabin-Karp 以滾動雜湊在文字上滑動視窗,省去大部分比較;字典樹讓單字共用共同前綴,使前綴查詢的時間只與單字長度成正比。這個主題在程式面試中也很常見。
建議先熟悉前綴、後綴與邊界(border)等術語,並親手驗證樸素比對為什麼慢。接著自己為一個小模式計算前綴函數表,實作 KMP 搜尋與字典樹,並和標準函式庫的結果對照驗證。最後再了解雜湊碰撞、Unicode 正規化等在實務上會讓結果出錯的陷阱。
預先計算模式每個前綴的最長邊界長度;不相符時不回退文字,而是跳到較短的邊界,因此能在 O(n + m) 時間內完成搜尋。
Rabin-Karp 以 O(1) 時間更新長度為 m 的視窗雜湊並與模式雜湊比較,只有雜湊相等時才逐字元確認。
沿著字元逐層往下走的樹,共同前綴只儲存一次,因此前綴查詢與自動完成的時間只與單字長度成正比。
Z 陣列在線性時間內求出每個位置開始的後綴與整個字串的最長共同前綴。處理真實文字時還要注意正規化與索引單位。
prefix_function 計算模式每個前綴的最長邊界長度,kmp_search 利用這張表由左到右只讀一次文字,收集所有起始位置。由於每次相符後都把 k 設為 pi[k - 1],像位置 0 和 2 這樣重疊的出現也能找到。執行 python string_search.py 即可輸出兩個結果。
string_search.py
def prefix_function(p: str) -> list[int]:
"""pi[i] = length of the longest proper border of p[:i + 1]."""
pi = [0] * len(p)
k = 0
for i in range(1, len(p)):
while k > 0 and p[i] != p[k]:
k = pi[k - 1]
if p[i] == p[k]:
k += 1
pi[i] = k
return pi
def kmp_search(text: str, pattern: str) -> list[int]:
"""Start positions of every (possibly overlapping) occurrence."""
if not pattern:
return list(range(len(text) + 1))
pi = prefix_function(pattern)
hits, k = [], 0
for i, ch in enumerate(text):
while k > 0 and ch != pattern[k]:
k = pi[k - 1]
if ch == pattern[k]:
k += 1
if k == len(pattern):
hits.append(i - k + 1)
k = pi[k - 1]
return hits
if __name__ == "__main__":
print(prefix_function("ababaca")) # [0, 0, 1, 2, 3, 0, 1]
print(kmp_search("abababcabab", "abab")) # [0, 2, 7]
python string_search.py共六章,帶你從安裝一步步認識 字串演算法 的核心概念。
在這裡提問、分享經驗,交流關於 字串演算法 的看法。
還沒有討論。來發起第一則吧。
0 則留言
登入 · 登入後即可留言。
來留下第一則留言吧。