BM算法原理與優(yōu)化實踐(二)
三、BM 算法實現(xiàn)與代碼解析
(一)預(yù)處理階段
壞字符表構(gòu)建:壞字符表(Bad Character Table)的構(gòu)建是預(yù)處理階段的關(guān)鍵任務(wù)之一。其目的是為了在匹配過程中,當(dāng)遇到不匹配的字符(即壞字符)時,能夠快速確定模式串應(yīng)該向右滑動的距離。在 Python 中,我們可以使用哈希表(字典)來實現(xiàn)這一功能 。
def build_bad_char_table(pattern):
bad_char = {}
for i in range(len(pattern) - 1):
bad_char[pattern[i]] = i
return bad_char
這段代碼通過遍歷模式串,將每個字符作為鍵,其在模式串中的位置作為值,存儲在哈希表bad_char中。例如,對于模式串 "ABABC",哈希表中會記錄'A': 0, 'B': 1, 'C': 4(這里只記錄到倒數(shù)第二個字符,因為最后一個字符在匹配時用于判斷是否完全匹配,不需要單獨記錄其位置)。這種方式使得在匹配過程中查詢壞字符的位置時間復(fù)雜度為\(O(1)\),極大地提高了匹配效率。
除了哈希表,也可以使用數(shù)組來存儲壞字符的位置信息。如果字符集是有限且已知的,例如 ASCII 字符集(共 128 個字符),可以創(chuàng)建一個大小為 128 的數(shù)組bc,初始值全部設(shè)為 -1 。然后遍歷模式串,對于每個字符pattern[i],將bc[ord(pattern[i])]設(shè)置為i。這樣在查詢時,通過bc[ord(bad_character)]即可獲取壞字符在模式串中的位置,同樣支持\(O(1)\)時間復(fù)雜度的字符查詢。
好后綴表構(gòu)建:好后綴表(Good Suffix Table)的構(gòu)建相對復(fù)雜,它需要考慮模式串中后綴與前綴的匹配關(guān)系,以及后綴在模式串中的其他匹配位置。構(gòu)建好后綴表的核心邏輯在于通過后綴匹配和前綴檢查,生成每個位置對應(yīng)的滑動距離。
def build_good_suffix_table(pattern):
m = len(pattern)
suffix = [-1] * m
prefix = [False] * m
for i in range(m - 1):
j = i
while j >= 0 and pattern[j] == pattern[m - 1 - i + j]:
j -= 1
suffix[m - 1 - i] = j + 1
for i in range(m - 1):
if suffix[i] == 0:
prefix[m - 1 - i] = True
for i in range(1, m):
j = m - 1 - suffix[i]
prefix[j] = True
good_suffix = [m] * m
for i in range(m - 2, -1, -1):
if prefix[i]:
good_suffix[i] = m - 1 - i
else:
good_suffix[i] = good_suffix[m - 1 - suffix[i]]
return good_suffix
在這段代碼中,首先通過一個雙重循環(huán)計算suffix數(shù)組,suffix[i]表示從模式串末尾開始長度為i + 1的后綴,其最長匹配后綴的長度(即從模式串開頭開始的相同子串長度)。例如,對于模式串 "ABABC",當(dāng)i = 0時,后綴為 "C",其最長匹配后綴長度為 0;當(dāng)i = 1時,后綴為 "BC",其最長匹配后綴長度為 0;當(dāng)i = 2時,后綴為 "ABC",其最長匹配后綴長度為 3(因為模式串開頭的 "ABC" 與之匹配),所以suffix[2] = 3 。
然后,通過檢查suffix數(shù)組,標(biāo)記出哪些位置的后綴是模式串的前綴,存儲在prefix數(shù)組中。最后,根據(jù)suffix和prefix數(shù)組生成good_suffix數(shù)組,good_suffix[i]表示當(dāng)在位置i匹配失敗時,根據(jù)好后綴規(guī)則模式串應(yīng)該向右滑動的距離。如果prefix[i]為True,說明從位置i開始的后綴是模式串的前綴,此時滑動距離為模式串長度減去i;否則,滑動距離為good_suffix[m - 1 - suffix[i]],即根據(jù)之前計算的相同后綴的滑動距離來確定。
(二)匹配階段
匹配階段是 BM 算法的核心執(zhí)行部分,它從主串的起始位置開始,將模式串與主串進(jìn)行對齊,并從模式串的末尾字符開始向前比較。
def boyer_moore_search(text, pattern):
n = len(text)
m = len(pattern)
bad_char = build_bad_char_table(pattern)
good_suffix = build_good_suffix_table(pattern)
s = 0
while s <= n - m:
j = m - 1
while j >= 0 and text[s + j] == pattern[j]:
j -= 1
if j < 0:
print(f"Pattern found at position {s}")
s += good_suffix[0]
else:
bad_char_shift = j - bad_char.get(text[s + j], -1)
good_suffix_shift = good_suffix[j]
s += max(bad_char_shift, good_suffix_shift)
在上述代碼中,外層循環(huán)控制模式串在主串中的滑動位置s。每次循環(huán)開始時,將模式串的末尾字符與主串中對應(yīng)的字符進(jìn)行比較(通過內(nèi)層循環(huán)實現(xiàn))。如果從模式串末尾開始的所有字符都與主串中對應(yīng)的字符匹配(即j < 0),則說明找到了一個匹配位置,打印出匹配位置,并根據(jù)好后綴表中的第一個值(good_suffix[0])滑動模式串,因為此時整個模式串都匹配,按照好后綴規(guī)則移動可以繼續(xù)尋找下一個可能的匹配位置 。
如果在比較過程中發(fā)現(xiàn)不匹配的字符,即壞字符,根據(jù)壞字符規(guī)則計算滑動距離bad_char_shift,同時根據(jù)好后綴規(guī)則計算滑動距離good_suffix_shift。然后取這兩個滑動距離中的較大值,將模式串向右滑動相應(yīng)的距離,繼續(xù)下一輪匹配。這樣,通過不斷地利用壞字符規(guī)則和好后綴規(guī)則,BM 算法能夠在主串中快速定位模式串的所有匹配位置,避免了大量不必要的字符比較操作,從而實現(xiàn)高效的字符串匹配。





