text = 'QQQA23B23'
pattern = 'B23'
# 長度
n = len(text)
m = len(pattern)
# 尾端對齊。從後面往前面比對
i = m - 1 # text索引值
j = m - 1 # pattern索引值
# 備份。比對失敗時使用。使i、j回到尾端
i0 = i
j0 = j
while i < n:
if text[i] == pattern[j]: # 比對成功
if j == 0:
return i
else:
i -= 1
j -= 1
else: # 比對失敗
i = i0 + 移動格數
j = j0
i0 = i
return -1
從後面往前比對
QQQA23
B23
A23B23
QQQA23
B23
A23B23
QQQA
23B23
A23B
23
比對失敗
壞字符是A
QQQA
23B23A23
B23
pattern往右
移動還沒比對的字
陸續經過壞字符A
如果某個字和壞字符相同,就可以對齊
QQQA
23B23
A
23B23
這是從後面往前面比對
的原因
j
是比對失敗位置
如果有找到相同的字移動格數 = j - 相同字的索引值
如果沒有找到相同的字移動格數 = j - (-1)
各種可能的壞字符
的ascii碼
假設壞字符只有ascii
pattern的索引值
「pattern裡面:和壞字符相同的字」的索引值
# 各種可能的壞字符的數量
NO_OF_CHARS = 256
def makeBadTable(pattern):
# 建立預設值
badTable = [-1] * NO_OF_CHARS
# pattern長度
patLen = len(pattern)
# 從索引值0開始、從左往右
# 如果pattern有兩個以上相同的字,右邊的位置優先、覆蓋已經設的值
for i in range(patLen - 1):
badTable[ord(pattern[i])] = i
# 取得「pattern裡面:和壞字符相同的字」的索引值
return badTable
j
是比對失敗位置
移動格數 = j - badTable[ord(text[i])]
移動格數有可能是負的
需要搭配好後綴規則
來決定移動格數
例如:
QQQAA3
BA3
AA3BA3
QQQAA3
BA3
AA3BA3
QQQA
A3BA3
AA3B
A3
比對失敗
QQQA
A3BA3
AA3BA
3
移動格數 -1 往左移動了
從後面往前比對
QQQA23
B23
A23B23
QQQA23
B23
A23B23
好後綴是23
QQQA23
B23A23B2
3
pattern往右
移動pattern[-1]左邊的字
陸續經過好後綴23
如果這些字和好後綴相同,就可以對齊
QQQA23
B23
A23
B23
這是從後面往前面比對
的原因
QQQA23
B23
B23B23
比對
QQQA23
B23
B23
B23
移動、對齊
QQQA23
B23
B
23B
23
左邊還有字
左邊的字一樣,重蹈覆轍,不能對齊,就像上次一樣
QQQA23
B23
A23B23
比對
QQQA23
B23
A23
B23
移動、對齊
QQQA23
B23
A
23B
23
左邊還有字
左邊的字不一樣,可以對齊
QQQ23
B23
23B23
QQQ23
B23
23
B23
移動之後
左邊沒有字,不用檢查了
QQ23
B23
3B23
QQ23
B23
3
B23
好後綴可能有許多字
以最右邊
那一個字的位置,代表好後綴的位置
總是m - 1
(pattern最後一個索引值)
與好後綴對齊的字,可能有許多字
以最右邊
那一個字的位置,代表對齊的位置
可能的對齊位置:0 到 (m - 2)
每個比對失敗位置 → 好後綴長度 → 每個可能對齊位置、逐一查找
每個可能對齊位置 → 它和它左邊的那些字,可以提供多少長度範圍
(給好後綴對齊用) →
根據長度範圍
算出比對失敗位置
如果長度範圍
沒有到達最左邊(索引值0),表示:左邊有一字比對失敗
這個位置用來「完全對齊」
只能對齊相同長度
好後綴
如果長度範圍
到達最左邊(索引值0),左邊內容全部都用來對齊了
這個位置除了「完全對齊」,還可以「部份對齊」
可以對齊
相同長度
好後綴 (完全對齊)大於此長度
好後綴 (部份對齊)無論長度範圍
是哪種情況
不能用來對齊小於此長度
好後綴
理由:對齊的情形 → 完全對齊(左邊還有字 - 情況1)
如果有找到對齊的位置移動格數 = (m - 1) - 對齊的位置
如果沒有找到對齊的位置,移動pattern長度
移動格數 = (m - 1) - (-1)
比對失敗位置:0 到 (m-1)
移動格數
def makeGoodTable(pattern):
# pattern長度
patLen = len(pattern)
# 建立預設值
defaultMove = patLen # (patLen - 1) - (-1)
goodTable = [defaultMove] * patLen
# 一開始就比對失敗,沒有好後綴,移動1格
goodTable[-1] = 1
# 好後綴位置
good = patLen - 1
# test是每一個可能對齊的位置
# 從左到右,較右邊位置優先,覆蓋已經設的值
for test in range(patLen - 1):
t = test
g = good
while t > -1:
if pattern[t] != pattern[g]:
break
t -= 1
g -= 1
'''
比對失敗位置| 好後綴左端 好後綴右端
g | g+1 good
|
t | t+1 test
| 對齊範圍左端 對齊範圍右端
'''
# t有移動
if t != test:
goodTable[g] = good - test
# t有移動,而且移到了最左邊
# 可以用來部份對齊 比對失敗位置[0:g]
if t == -1:
goodTable[0:g] = [good - test] * g
return goodTable
# 各種可能的壞字符的數量
NO_OF_CHARS = 256
def makeBadTable(pattern):
# 建立預設值
badTable = [-1] * NO_OF_CHARS
# pattern長度
patLen = len(pattern)
# 從索引值0開始、從左往右
# 如果pattern有兩個以上相同的字,右邊的位置優先、覆蓋已經設的值
for i in range(patLen - 1):
badTable[ord(pattern[i])] = i
# 取得「pattern裡面:和壞字符相同的字」的索引值
return badTable
def makeGoodTable(pattern):
# pattern長度
patLen = len(pattern)
# 建立預設值
defaultMove = patLen # (patLen - 1) - (-1)
goodTable = [defaultMove] * patLen
# 一開始就比對失敗,沒有好後綴,移動1格
goodTable[-1] = 1
# 好後綴位置
good = patLen - 1
# test是每一個可能對齊的位置
# 從左到右,較右邊位置優先,覆蓋已經設的值
for test in range(patLen - 1):
t = test
g = good
while t > -1:
if pattern[t] != pattern[g]:
break
t -= 1
g -= 1
'''
比對失敗位置| 好後綴左端 好後綴右端
g | g+1 good
|
t | t+1 test
| 對齊範圍左端 對齊範圍右端
'''
# t有移動
if t != test:
goodTable[g] = good - test
# t有移動,而且移到了最左邊
# 可以用來部份對齊 比對失敗位置[0:g]
if t == -1:
goodTable[0:g] = [good - test] * g
return goodTable
def BM(text, pattern):
# 長度
n = len(text)
m = len(pattern)
# 尾端對齊。從後面往前面比對
i = m - 1 # text索引值
j = m - 1 # pattern索引值
# 備份。比對失敗時使用。使i、j回到尾端
i0 = i
j0 = j
# 製造表格
badTable = makeBadTable(pattern)
goodTable = makeGoodTable(pattern)
while i < n:
if text[i] == pattern[j]: # 比對成功
if j == 0:
return i
else:
i -= 1
j -= 1
else: # 比對失敗
i = i0 + max(j - badTable[ord(text[i])], goodTable[j])
j = j0
i0 = i
return -1
if __name__ == '__main__':
# 這個版本的BM只能找ascii
text = '49 59 69 45 55 65'
pattern = '65'
r = BM(text, pattern)
print('match at %s' % r)
i值
的兩種使用方式 text[i] == pattern[j]
text[i+j] == pattern[j]
理論版
badTable[比對失敗位置][ascii碼]
實際版
badTable[ascii碼]
有三種
pattern索引值(壞字符相同字所在處)
預設值:-1
移動格數
預設值:m
計算方式:m - 1 - pattern索引值
移動格數 + i值回到尾端距離
計算方式:m - 1 - pattern索引值
m - 1 - pattern索引值
的意思一開始就比對失敗
的移動格數各種比對失敗位置的移動格數 = 一開始就比對失敗的移動格數 - (j0 - j)
不管是在哪一個位置比對失敗,就當作是一開始就比對失敗
,壞字符是text[i0]
(Boyer–Moore–Horspool algorithm)
移動格數 + i值回到尾端距離
與text[i] == pattern[j]
搭配使用
原本:i = i0 + 移動格數
現在:i = i + (移動格數 + i值回到尾端距離)
有兩種
移動格數
預設值:m
移動格數 + i值回到尾端距離
與text[i] == pattern[j]
搭配使用
原本:i = i0 + 移動格數
現在:i = i + (移動格數 + i值回到尾端距離)
表格的值常常大於pattern長度