2019-11-06

Boyer-Moore字串搜尋演算法

注意事項:

-eq 不區分大小寫。如果有需要,請改用 -ceq
PS C:\> 'a'  -eq  'A'
True
PS C:\> 'a'  -ceq  'A'
False

在 $Text 裡面找 $Key ( 1個字 )

$Text = 'QQQQQBCABC'     # 10字
$Key = 'B'

for ( $base = 0 ; $base -lt 10 ; $base++ ){ # 10 是 $Text的長度
    if ( $Key -eq $Text[$base] ){ break }
}

if ($base -eq 10){ # 10 是 $Text的長度
    '沒找到'
}else{
    "$Key$base"
}

在 $Text 裡面找 $Key ( 2個字 or 2個字以上)

$Text = 'QQQQQBCABC'    # 10字
$Key = 'BCABC'              # 5字

for ( $base = 0 ; $base -lt 6 ; $base++ ){ # $Text長度 - ($Key長度 - 1 )
                                            #      10   - ( 5 - 1 ) = 6
    for ($i = 0 ; $i -lt 5 ; $i++){ # 5 是  $Key的長度
        if ( $Key[$i]  -ne  $Text[$base + $i] ){ break }
    }

    if ($i -eq 5){ break }
}

if ($base -eq 6){
    '沒找到'
}else{
    "$Key$base"
}

前例的另一種寫法:第二層 for 改成「後往前」找

$Text = 'QQQQQBCABC'
$Key = 'BCABC'

for ( $base = 0 ; $base -lt 6 ; $base++ ){

    for ($i = 4 ; $i -ge 0 ; $i--){ # $i  4 到 0
        if ( $Key[$i]  -ne  $Text[$base + $i] ){ break }
    }

    if ($i -eq -1 ){ break } # $i -eq -1
}

if ($base -eq 6){
    '沒找到'
}else{
    "$Key$base"
}

把 第一層 for 改成 while

$Text = 'QQQQQBCABC'
$Key = 'BCABC'

$base = 0      # $base = 0
while ($base -lt 6){   # $base -lt 6

    for ($i = 4 ; $i -ge 0 ; $i--){
        if ( $Key[$i]  -ne  $Text[$base + $i] ){ break }
    }

    if ($i -eq -1 ){ break }
    $base += 1       #  $base += 1
}

if ($base -eq 6){
    '沒找到'
}else{
    "$Key$base"
}

Boyer-Moore字串搜尋演算法

$Text = 'QQQQQBCABC'
$Key = 'BCABC'

<#
1.
「$base 移動方向」必須和「$i 比對方向」相反
根據「$base 移動方向」,來決定「$i 比對方向」
當 $base 移動方向 →     則 $i 比對方向  ←

2.
根據「$i 比對方向」和「$Key」來製表 $Bc 和 $Gs

3.
比對失敗時,使用 $Bc $Gs 兩者之間的最大值,來改變 $base
#>

$Bc = $Gs = $null
<# 步驟 2
此處設定 $Bc 和 $Gs
#>

$base = 0
while ($base -lt 6){

    #步驟 1  方向相反
    for ($i = 4 ; $i -ge 0 ; $i--){
        if ( $Key[$i]  -ne  $Text[$base + $i] ){ break }
    }

    if ($i -eq -1 ){ break }
    
    #步驟 3   移動幾格 
    [int]$badChar = $Text[$base + $i]

    if ( $Bc[$i][ $badChar ]  -gt $Gs[$i] ){
        $base += $Bc[$i][ $badChar ] 
    }else{
        $base += $Gs[$i]
    }
}

if ($base -ge 6){
    '沒找到'
}else{
    "$Key$base"
}

BM:從前面往後找

$Text = 'QQQQQBCABC'
$Key = 'BCABC'

if ($Key.length -lt 2 -or $Text.Length -lt $Key.Length ){'bye';pause;exit}

#===== 製表 $Bc =====
$Bc = @( $Key.Length ) * 64KB

for ( $i = 0 ; $i -le $Key.Length - 2  ; $i++ ){
    [int]$badChar = $Key[$i]
    $Bc[ $badChar ] = $Key.Length - 1 - $i
}

#===== 製表 $Gs =====
$Gs = @( $Key.Length ) * $Key.Length
$good = $Key.Length - 1
$defaultMove = $Key.Length

for($test = 0; $test -le $Key.Length - 2; $test++){
    $g = $good
    $t = $test

    for ( ; $t -ge 0 ; $g--,$t--){
        if ($Key[$g] -ne $Key[$t]) {break}
    }

    if ($t -eq $test){
        $Gs[$good - $test -1 ] = $defaultMove
    }elseif($t -eq -1){
        $Gs[$g] = $good - $test

        $defaultMove = $good - $test
    }else{
        $Gs[$g] = $good - $test
    }
}

$Gs[$Key.Length - 1] = 1

#==============
# 如果$Key是固定的,可以事先算好,把$Bc $Gs內容直接寫在 ps1
# 就可以不用臨時製表了
# $BcStr = "$Bc" -replace ' ', ','
# $GsStr = "$Gs" -replace ' ', ','

#===== 開始尋找 =====
$base = 0
while ($base -lt $Text.length - $Key.Length + 1){

    # 方向相反
    for ($i = $Key.Length - 1 ; $i -ge 0 ; $i--){
        if ( $Key[$i]  -ne  $Text[$base + $i] ){ break }
    }

    if ($i -eq -1 ){ break }
    
    # 移動幾格 
    [int]$badChar = $Text[$base + $i]
    $B_move = $Bc[ $badChar ] - ($Key.Length - 1 - $i)
    #有時 $B_move 值 會是負的。這時需要靠 $Gs[$i]

    if ( $B_move  -gt $Gs[$i] ){
        $base += $B_move
    }else{
        $base += $Gs[$i]
    }
}
#===== 結果 =====

if ($base -ge $Text.length - $Key.Length + 1){
    "$Key  not found"
}else{
    "$Key  in  $base" 
}

pause

BM:從後面往前找

$Text = 'BCABCQQQQQ'
$Key = 'BCABC'

if ($Key.length -lt 2 -or $Text.Length -lt $Key.Length ){'bye';pause;exit}
#===== 製表 $Bc =====
$Bc = @( $Key.Length ) * 64KB

for ( $i = $Key.Length - 1 ; $i -ge 1  ; $i-- ){
    [int]$badChar = $Key[$i]
    $Bc[ $badChar ] =  $i - 0
}

#===== 製表 $Gs ===== $Gs = @( $Key.Length ) * $Key.Length $good = 0 $defaultMove = $Key.Length for($test = $Key.Length - 1; $test -ge 1; $test--){ $g = $good $t = $test for ( ; $t -le $Key.Length - 1 ; $g++,$t++){ if ($Key[$g] -ne $Key[$t]) {break} } if ($t -eq $test){ $Gs[$Key.Length - $t ] = $defaultMove }elseif($t -eq $Key.Length){ $Gs[$g] = $test - $good $defaultMove = $test - $good }else{ $Gs[$g] = $test - $good } } $Gs[0] = 1 #============== # 如果$Key是固定的,可以事先算好,把$Bc $Gs內容直接寫在 ps1 # 就可以不用臨時製表了 # $BcStr = "$Bc" -replace ' ', ',' # $GsStr = "$Gs" -replace ' ', ',' #===== 開始尋找 ===== $base = $Text.length - 1 - ($Key.Length - 1) while ($base -ge 0){ # 方向相反 for ($i = 0 ; $i -le $Key.Length - 1 ; $i++){ if ( $Key[$i] -ne $Text[$base + $i] ){ break } } if ($i -eq $Key.Length ){ break } # 移動幾格 [int]$badChar = $Text[$base + $i] $B_move = $Bc[ $badChar ] - $i #有時 $B_move 值 會是負的。這時需要靠 $Gs[$i] if ( $B_move -gt $Gs[$i] ){ $base -= $B_move }else{ $base -= $Gs[$i] } } #===== 結果 ===== if ($base -lt 0){ "$Key not found" }else{ "$Key in $base" } pause

相關文章:


參考資料:

沒有留言:

張貼留言