- 帖子
- 36
- 积分
- 105
- 技术
- 12
- 捐助
- 0
- 注册时间
- 2013-11-8
|
2楼
发表于 2020-10-11 11:12
| 只看该作者
本帖最后由 nwm310 于 2020-10-11 12:26 编辑
KMP 字串搜寻演算法 - 如何移动?
一开始就比对失败:
比对 移动后
$i 01234567 01234567
QBAQABAB QBAQABAB
X
ABAB ABAB
$j 0123 0123
$i = 1 $i = 2
$j = 0 $j = 0
一开始就比对失败,此时的 $j 是 0
看到 $j 是 0,就移动1格。
移动1格的方法: $i++ 、 $j 不变(仍是0)
比对成功几字之后,然后才比对失败:
比对
$i 01234567
ABAQABAB
|||X
ABAB
$j 0123
1.在 $j 索引3比对失败
2.前面3个字是相同的,「相同区」长度为3,内容为ABA
3.「相同区」长度 = 比对失败索引值($j)
如果在索引1比对失败,那麽「相同区」长度就是1
如果在索引2比对失败,那麽「相同区」长度就是2
4.在「相同区」里面找移动之后的「次相同区」(上下要相同)
移动前 移动后 错误移法(上下不同)
ABA ABA ABA
ABA ABA ABA
「次相同区」长度为1,内容为A
移动格数 = 「相同区」长度 - 「次相同区」长度
= 3 - 1
= 2
移动前 移动后
$i 01234567 01234567
ABAQABAB ABAQABAB
|||X |
ABAB ABAB
$j 0123 0123
比对失败位置 继续比对位置
$i = 3 $i = 3
$j = 3 $j = 1
移动2格之后,并且跳过1格不比对
「次相同区」长度,等於「跳过不比对」的长度
原本比对失败的 旧$j 为3
即将参与比对的 新$j 为1
新$j = 「次相同区」长度
知道「次相同区」长度,就可得知 新$j
下次比对时,参与比对的 $i 没变(仍和上次一样)
但 $j 变小了,藉此达成「右移」的效果
因为 $i 没有变、不会向后退:
可以不使用 $base + $j 的方式来取得 $i
不再关注「移动几格」
而是关注「次相同区」长度
普通移法的 $i 会向后退:
$i 移动前 移动后
01234567 01234567
ABABAQABAB ABAQABAB
|||X
ABAB ABAB
$j 0123 0123
$i = 3 比对失败 从 $i = 1 继续比对。
$i 改变了、向后退了
5.「相同区」长度为1,「次相同区」长度一定是0
移动前 移动后
A A
A A
长度太短,无法去找「次相同区」
「次相同区」长度为0
移动格数 = 「相同区」长度 - 「次相同区」长度
= 1 - 0
= 1
6.别的例子
移动前 移动后 错误移法(上下不同)
ABB ABB ABB
ABB ABB ABB
「相同区」长度为3
「次相同区」长度为0
移动格数 = 「相同区」长度 - 「次相同区」长度
= 3 - 0
= 3
移动3格之后,即将参与比对的索引值$j 为0
$j 等於 「次相同区」长度
7.别的例子
移动前 移动后 错误移法(上下不同)
ABAA ABAA ABAA
ABAA ABAA ABAA
「相同区」长度为4
「次相同区」长度为1
移动格数 = 「相同区」长度 - 「次相同区」长度
= 4 - 1
= 3
移动3格之后,可以跳过1格不比对
「次相同区」长度,等於跳过不比对的长度
即将参与比对的索引值$j 为1
$j 等於 「次相同区」长度
8.别的例子
移动前 移动后 错误移法(上下不同)
ABAB ABAB ABAB
ABAB ABAB ABAB
「相同区」长度为4
「次相同区」长度为2
移动格数 = 「相同区」长度 - 「次相同区」长度
= 4 - 2
= 2
移动2格之后,可以跳过2格不比对
「次相同区」长度,等於跳过不比对的长度
即将参与比对的索引值$j 为2
$j 等於 「次相同区」长度
|
KMP 字串搜寻演算法 - $base 「移几格」版
$Text = 'ABAQABAB'
$Key = 'ABAB'
$f = @(0) * $Key.Length
<#
$f 是一个数组,它的长度和 $Key 一样
索引值 为 比对失败位置(也等於相同区长度)
$f[ 比对失败位置 ] 得到 「次相同区」长度
$f[ 旧$j ] 得到 新$j
$f[0] = -1 这个值在本程式里不会使用到
$f[1] = 0 相同区长度是 1 次相同区长度一定是 0
此处设定 $f数组 其他元素内容
#>
$base = 0
$baseEnd = $Text.Length - ($Key.Length - 1)
$j = 0
while ($base -lt $baseEnd){
for ( ; $j -lt $Key.Length ; $j++){ # 这里没有设定 $j 的初始值
if ( $Key[$j] -ne $Text[$base + $j] ){ break }
}
if ($j -eq $Key.Length){ break } # 找到了
if ($j -eq 0){$base += 1; continue} # 一开始就比对失败
$base += $j - $f[$j] # 移几格? 「相同区」长度 - 「次相同区」长度
$j = $f[$j] # 设定 新$j 。 「次相同区」长度、用来跳过不比对
}
if ($base -eq $baseEnd){
'没找到'
}else{
"$Key 在 $base"
}
|
KMP 字串搜寻演算法 - 「$i vs $j」版
$Text = 'ABAQABAB'
$Key = 'ABAB'
$f = @(0) * $Key.Length
<#
$f 是一个数组,它的长度和 $Key 一样
$f[旧$j ] 得到 新$j
此处设定 $f 元素内容
#>
$i = 0
$j = 0
$ans = -1
while ($i -lt $Text.Length){ # 此处和前例不同
if ($Text[$i] -eq $Key[$j]){ # 比对成功
$i++
$j++
if ($j -eq $Key.Length){ # 找到了
$ans = $i - $j # 注意:要用减法
break
}
}else{
if ($j -ne 0){ # 有「相同区」
$j = $f[$j] # 以「次相同区」长度 设定 新$j。
# $i 不变
}else{# 一开始就比对失败。$j是0、不用设定
$i++
}
}
}
if ($ans -eq -1){ # 此处和前例不同
'没找到'
}else{
"$Key 在 $ans"
}
|
|
|