好後綴規則 (長度 1)
比對方向 ←
01234
QQQQCCCABC
X|
CCABC
01234
移動方向 →
壞字符:Q
壞字符位置:3
好後綴:C (壞字符右邊的字。位於$Text。 $Key也有相同的字 )
好後綴位置:4 (總是 最大索引值 4)
尋找範圍:
在後續移動過程中,仍有機會和「$Text裡的好後綴C」對齊的字:
$Key[0]到 $Key[3]
(那些字總是位於 $Key最後一個字的左邊 )
和好後綴相同的字的位置:1
額外檢查:
位置 1 的左邊的字:C
「$Key裡的好後綴」的左邊的字:B
(它和壞字符比對失敗過)
以上兩者不相同,所以可以移到 位置1
移動格數:好後綴位置 - 找到的位置
4 - 1 = 3
QQQQCCCABC
|
CCABC
找到C對齊,移動3格
有 2 個 C
找距離「好後綴」最近的那個
額外檢查失敗的例子
比對方向 ←
01234
QQQQCBCABC
X
BCABC
01234
移動方向 →
壞字符:Q
壞字符位置:3
好後綴:C (壞字符右邊的字。位於$Text。 $Key也有相同的字 )
好後綴位置:4 (總是 最大索引值 4)
尋找範圍:
在後續移動過程中,仍有機會和「$Text裡的好後綴C」對齊的字:
$Key[0]到 $Key[3]
(那些字總是位於 $Key最後一個字的左邊 )
和好後綴相同的字的位置:1
額外檢查:
位置 1 的左邊的字:B
「$Key裡的好後綴」的左邊的字:B
(它和壞字符比對失敗過)
以上兩者相同,所以不可以移到 位置 1
QQQQCBCABC
X|
BCABC
勉強移到 位置 1的結果
C可以對齊
但B不行。就像上次那樣
QQQQCBCABC
|
BCABC
最後,沒有找到可以對齊的
移動格數:4 - (-1) = 5
左邊沒有東西、不用額外檢查
比對方向 ←
01234
QQQQCCAABC
X
CAABC
01234
移動方向 →
壞字符:Q
壞字符位置:3
好後綴:C (壞字符右邊的字。位於$Text。 $Key也有相同的字 )
好後綴位置:4 (總是 最大索引值 4)
尋找範圍:
在後續移動過程中,仍有機會和「$Text裡的好後綴C」對齊的字:
$Key[0]到 $Key[3]
(那些字總是位於 $Key最後一個字的左邊 )
和好後綴相同的字的位置:0
位置 0 的左邊的字:沒有
不用額外檢查了
移動格數:好後綴位置 - 找到的位置
4 - 0 = 4
QQQQCCAABC
|
CAABC
找到C對齊,移動4格
好後綴規則(長度 1) vs 壞字符規則
兩者很像。以下是它們的差別。
$i 為比對失敗位置
尋找範圍:
Bc:$Key[$i]的左邊
Gs:$Key[-1]的左邊 ($Key最後一個字的左邊)
符合的條件:
Bc:和「壞字符」一樣
Gs:
1. 和「好後綴」($Key最後一個字) 一樣
2. 需要作「額外檢查」
需不需要去猜「壞字符」是什麼?
Bc:需要。
因此,製表時需要較大的空間
$B4 = @( 4 - (-1) ) * 256
使用表格時,需要知道「壞字符」是什麼
[int]$badChar = $Text[$base + $i]
if ( $Bc[$i][ $badChar ] -gt $Gs[$i] )
Gs:不需要。
好後綴規則 (長度 2)
比對方向 ←
012345
QQQQBCBBCABC
X
BBCABC
012345
移動方向 →
壞字符:Q
壞字符位置:3
好後綴:BC (壞字符右邊的那些字。位於$Text。 $Key也有相同的字 )
好後綴位置:5 (總是 最大索引值 5)
(它有兩個字,以5來代表)
尋找範圍:
在後續移動過程中,仍有機會和「$Text裡的好後綴BC」對齊的字:
$Key[0]到 $Key[4]
(那些字總是位於 $Key最後一個字的左邊 )
和好後綴BC相同的字的位置:2
(它有兩個字,以索引2來代表)
額外檢查:
位置 2(和位置1)的左側的字:B
「$Key裡的好後綴」的左側的字:A
(它和壞字符比對失敗過)
以上兩者不相同,所以可以移到 位置 2
移動格數:好後綴位置 - 找到的位置
5 - 2 = 3
012345
QQQQBCBBCABC
||
BBCABC
找到BC,而且左邊不同
移動格數:5 - 2 = 3
額外檢查失敗的例子
比對方向 ←
012345
QQQQBCABCABC
X
ABCABC
012345
移動方向 →
壞字符:Q
壞字符位置:3
好後綴:BC (壞字符右邊的那些字。位於$Text。 $Key也有相同的字 )
好後綴位置:5 (總是 最大索引值 5)
(它有兩個字,以5來代表)
尋找範圍:
在後續移動過程中,仍有機會和「$Text裡的好後綴BC」對齊的字:
$Key[0]到 $Key[4]
(那些字總是位於 $Key最後一個字的左邊 )
和好後綴BC相同的字的位置:2
(它有兩個字,以索引2來代表)
額外檢查:
位置 2(和位置1)的左側的字:A
「$Key裡的好後綴」的左側的字:A
(它和壞字符比對失敗過)
以上兩者相同,所以不可以移到 位置 2
QQQQBCABCABC
X||
ABCABC
勉強移到 位置 2的結果
BC可以對齊
但A不行。就像上次那樣
QQQQBCABCABC
|
ABCABC
最後,沒有找到可以對齊的
移動格數:5 - (-1) = 6
左邊沒有東西、不用額外檢查
比對方向 ←
012345
QQQQBCBCCABC
X
BCCABC
012345
移動方向 →
壞字符:Q
壞字符位置:3
好後綴:BC (壞字符右邊的那些字。位於$Text。 $Key也有相同的字 )
好後綴位置:5 (總是 最大索引值 5)
(它有兩個字,以5來代表)
尋找範圍:
在後續移動過程中,仍有機會和「$Text裡的好後綴BC」對齊的字:
$Key[0]到 $Key[4]
(那些字總是位於 $Key最後一個字的左邊 )
和好後綴BC相同的字的位置:1
(它有兩個字,以索引1來代表)
額外檢查:
位置 1(和位置0)的左側的字:沒有
不用額外檢查了
移動格數:好後綴位置 - 找到的位置
5 - 1 = 4
012345
QQQQBCBCCABC
||
BCCABC
找到BC
BC左邊沒有東西,不用額外檢查
移動格數:5 - 1 = 4
部份好後綴、不用額外檢查
比對方向 ←
012345
QQQQBCCABABC
X
CABABC
012345
移動方向 →
壞字符:Q
壞字符位置:3
好後綴:BC (壞字符右邊的那些字。位於$Text。 $Key也有相同的字 )
好後綴位置:5 (總是 最大索引值 5)
(它有兩個字,以5來代表)
尋找範圍:
在後續移動過程中,仍有機會和「$Text裡的好後綴BC」對齊的字:
$Key[0]到 $Key[4]
(那些字總是位於 $Key最後一個字的左邊 )
和好後綴BC相同的字的位置:沒有
和「部份好後綴C」相同的字的位置:0
額外檢查:
因為是部份好後綴,不用額外檢查了
移動格數:好後綴位置 - 找到的位置
5 - 0 = 5
012345
QQQQBCCABABC
|
CABABC
當「完整版的後綴」找不到,「部份版的」也可以
只對齊C即可
移動格數:5 - 0 = 5
找到的對齊位置有兩種
1.通過額外檢查的位置
不能對齊「其他長度的好後綴」
比對方向 ←
0123456
QQQQQBCBBBCABC
X
BBBCABC
移動方向 →
找到的位置3:長度 2 好後綴專用。不能對齊「其他長度的好後綴」
長度 2 好後綴
0123456
QQQQQBCBBBCABC
||
BBBCABC
長度 1 好後綴
0123456
QQQQQQCBBBCABC
X|
BBBCABC
長度 3 好後綴
0123456
QQQQABCBBBCABC
X||
BBBCABC
長度 4 好後綴
0123456
QQQCABCBBBCABC
X||
BBBCABC
2.不用額外檢查的位置
(左邊沒有東西)
「長度大於、等於它」的好後綴可以和它對齊
「長度 小於 它 」的好後綴不能和它對齊
比對方向 ←
0123456
QQQQQBCBCACABC
X
BCACABC
移動方向 →
「長度 大於、等於 2 」的好後綴可以和它對齊
「長度 小於 2 」的好後綴不能和它對齊
長度 2 好後綴
0123456
QQQQQBCBCACABC
||
BCACABC
長度 1 好後綴
0123456
QQQQQQCBCACABC
X|
BCACABC
長度 3 好後綴
0123456
QQQQABCBCACABC
||
BCACABC
長度 4 好後綴
0123456
QQQCABCBCACABC
||
BCACABC
開始製表 $Gs
方法一:
根據比對失敗位置,計算好後綴長度
在可能的對齊位置,逐一確認:是否和好後綴相同?
$Key = 'BCABC'
$Gs = @( 5 ) * 5
<# 結果:
$Gs[0] = $Gs[1] = $Gs[2] = $Gs[3] = $Gs[4] = 5
$Gs是一個陣列、有5個元素。每個元素的值都是5
元素的索引值:比對失敗位置
元素的值:比對失敗發生時,要移動的距離
先假設找不到可以對齊的位置
移動距離 = 好後綴位置 - 找不到
= 4 - (-1)
= 5
位置 4 沒有好後綴,稍後會再設值 $Gs[4] = 1
#>
#========= 設定 $Gs[3] ===========
#====== step 1:根據比對失敗位置,計算好後綴長度=====
#好後綴位置 = 總是最後面的索引值
$good = 4
#好後綴長度 = 好後綴位置 - 比對失敗位置
$len = 4 - 3
#====== step 2:在可能的對齊位置,逐一確認:是否和好後綴相同?=====
# $test 是可能的對齊位置 從 位置 3 開始找
# 位置 3 = 好後綴位置的左邊 = $good - 1
for($test = 3; $test -ge 0; $test--){
$g = $good
$t = $test
# $L -gt 0 表示:好後綴 還沒比對完
# $t -ge 0 表示:$Key[$t]還有字可以參加比對
# 如果 $t -lt 0 $Key[$t]已經沒有字可以參加比對
# 儘管 $L -gt 0 好後綴還沒比對完,也會停止比對
# 不斷使$g 和 $t 向左移動、進行比對
for ($L = $len ; $L -gt 0 -and $t -ge 0 ; $g--,$t--,$L--){
# 如果比對失敗,這個位置不可以對齊
if ($Key[$g] -ne $Key[$t]) {break}
}
#======== $Key[$t]已經沒有字可以參加比對 =======
# $t 對齊範圍左端 對齊範圍右端
# -1 0 $test
#
# $g $g + 1 $good
# 好後綴的左端 好後綴的右端
# 也可以寫成 $Gs[3] = $g - $t
if ($t -eq -1) { $Gs[3] = $good - $test ;break}
#============ 好後綴比對完成 ===============
# 而且,$Key[$t]還有字可以參加比對
# 檢查左側是否「不相同」
# 此時的 $t $g 為左側
# 左側 對齊範圍左端 對齊範圍右端
# $t $t + 1 $test
#
# $g $g + 1 $good
# 左側 好後綴的左端 好後綴的右端
#
# 也可以寫成 $Gs[3] = $g - $t
#
# 此時的 $g 也是 比對失敗位置
# 也可以寫成 $Gs[$g] = $g - $t
if ($L -eq 0) {
if ($Key[$g] -ne $Key[$t]) {$Gs[3] = $good - $test ;break }
}
}
#上面只是$Gs[3]的設值,還有$Gs[2] $Gs[1] $Gs[0]和上例類似
#位置 4沒有好後綴
$Gs[4] = 1
方法二:
確認每個可能的對齊位置(從右邊開始 ),可以符合多少後綴長?
藉由所符合的後綴長,推得在哪一個「比對失敗位置」發生時,可以和好後綴對齊
$Key = 'BCABC'
$Gs = @( 5 ) * 5
<# 結果:
$Gs[0] = $Gs[1] = $Gs[2] = $Gs[3] = $Gs[4] = 5
$Gs是一個陣列、有5個元素。每個元素的值都是5
元素的索引值:比對失敗位置
元素的值:比對失敗發生時,要移動的距離
先假設找不到可以對齊的位置
移動距離 = 好後綴位置 - 找不到
= 4 - (-1)
= 5
位置 4 沒有好後綴,稍後會再設值 $Gs[4] = 1
#>
#好後綴位置 = 總是最後面的索引值
$good = 4
# $test 是可能的對齊位置 從 位置 3 開始找
# 位置 3 = 好後綴位置的左邊 = $good - 1
for($test = 3; $test -ge 0; $test--){
$g = $good
$t = $test
# 使$g 和 $t 持續向左移動、進行比對
# $t 可能會到達最左邊(接下來,就沒有字了)
for ( ; $t -ge 0 ; $g--,$t--){
if ($Key[$g] -ne $Key[$t]) {break}
}
if ($t -eq $test){
# ===== 可能 1 :$t根本沒移動。符合 0 個 後綴長 ======
}elseif($t -eq -1){
# === 可能 2 :$t 已經到達 $Key的最前面了,沒有字可供繼續比對了===
# 符合的後綴長 等於 ($test + 1) 也等於 ($good - $g)
# 比對失敗位置 等於 好後綴位置 - 符合的後綴長
# 等於 $good - ($test + 1)
# 也等於 $g
# $t 對齊範圍左端 對齊範圍右端
# -1 0 $test
#
# $g $g + 1 $good
# 比對失敗位置 好後綴的左端 好後綴的右端
#如果仍是預設值,才設值
if ($Gs[$g] -eq 5){$Gs[$g] = $good - $test}
# 比對失敗位置 $g 左邊的那些位置: 0 到 ($g - 1)
# 所需要對齊的後綴長 大於 $test所提供的長度
# $test 可以和這些比對失敗位置的好後綴對齊
for ($i = 0; $i -lt $g; $i++){
#如果仍是預設值,才設值
if ($Gs[$i] -eq 5){$Gs[$i] = $good - $test}
}
}else{
#===== 可能 3 :$t 移動了幾格。符合了幾格後綴長 ======
# $t 介於 「完全不動」和「沒有字可比對」 之間
# 符合的後綴長 等於 ($test - $t) 也等於 ($good - $g)
# 比對失敗位置 等於 好後綴位置 - 符合的後綴長
# 等於 $good - ($test -$t)
# 也等於 $g
# 左側不同 對齊範圍左端 對齊範圍右端
# $t $t + 1 $test
#
# $g $g + 1 $good
# 比對失敗位置 好後綴的左端 好後綴的右端
#如果仍是預設值,才設值
if ($Gs[$g] -eq 5){$Gs[$g] = $good - $test}
}
}
#位置 4沒有好後綴
$Gs[4] = 1
方法三:
確認每個可能的對齊位置(從最左邊開始 ),可以符合多少後綴長?
藉由所符合的後綴長,推得在哪一個「比對失敗位置」發生時,可以和好後綴對齊
$Key = 'BCABC'
$Gs = @( 5 ) * 5
<# 結果:
$Gs[0] = $Gs[1] = $Gs[2] = $Gs[3] = $Gs[4] = 5
$Gs是一個陣列、有5個元素。每個元素的值都是5
元素的索引值:比對失敗位置
元素的值:比對失敗發生時,要移動的距離
先假設找不到可以對齊的位置
移動距離 = 好後綴位置 - 找不到
= 4 - (-1)
= 5
位置 4 沒有好後綴,稍後會再設值 $Gs[4] = 1
#>
#好後綴位置 = 總是最後面的索引值
$good = 4
#移動距離的預設值
$defaultMove = 5
# $test 是可能的對齊位置,從最左邊開始找。
for($test = 0; $test -le 3; $test++){
$g = $good
$t = $test
# 使$g 和 $t 持續向左移動、進行比對
# $t 可能會到達最左邊(接下來,就沒有字了)
for ( ; $t -ge 0 ; $g--,$t--){
if ($Key[$g] -ne $Key[$t]) {break}
}
if ($t -eq $test){
# ===== 可能 1 :$t根本沒移動。符合 0 個 後綴長 ======
# 原本想要符合的後綴長 ($test + 1 )
# 比對失敗位置 等於 好後綴位置 - 符合的後綴長
# 等於 $good - ($test + 1 )
# 使用預設值
$Gs[$good - $test -1 ] = $defaultMove
}elseif($t -eq -1){
# === 可能 2 :$t 已經到達 $Key的最前面了,沒有字可供繼續比對了===
# 符合的後綴長 等於 ($test + 1) 也等於 ($good - $g)
# 比對失敗位置 等於 好後綴位置 - 符合的後綴長
# 等於 $good - ($test + 1)
# 也等於 $g
# $t 對齊範圍左端 對齊範圍右端
# -1 0 $test
# $g $g + 1 $good
# 比對失敗位置 好後綴的左端 好後綴的右端
$Gs[$g] = $good - $test
#修改移動距離的預設值
$defaultMove = $good - $test
# 比對失敗位置 $g 左邊的那些位置: 0 到 ($g - 1)
# 所需要對齊的後綴長 大於 $test所提供的長度
# $test 可以和這些比對失敗位置的好後綴對齊
# $Gs[0] 到 $Gs[$g - 1] 都可以設值為 $good - $test
# 在此先不設值,而是把值先存到 $defaultMove
# $test 在之後的回合裡,會向右移動
# 會陸續設定 $Gs[$g - 1] 到 $Gs[0]
# 等到沒找到更適合的值,再使用 $defaultMove
}else{
#===== 可能 3 :$t 移動了幾格。符合了幾格後綴長 ======
# 此處的$t 介於 「完全不動」和「$Key最前面」 之間
# 符合的後綴長 等於 ($test - $t) 也等於 ($good - $g)
# 比對失敗位置 等於 $good - ($test -$t)
# 也等於 $g
# 左側不同 對齊範圍左端 對齊範圍右端
# $t $t + 1 $test
#
# $g $g + 1 $good
# 比對失敗位置 好後綴的左端 好後綴的右端
$Gs[$g] = $good - $test
}
}
#位置 4沒有好後綴
$Gs[4] = 1
沒有留言:
張貼留言