Knuth-Morris-Pratt 算法旨在查找字符串中子字符串的第一次(也可能是下一次)出现。由于子字符串可以包含重复部分,因此它使用某种回溯机制。这是伪代码中的算法:
let m ← 0, i ← 0
while m + i < length(S) do
if W[i] = S[m + i] then
if i = length(W) - 1 then
return m
let i ← i + 1
else
if T[i] > -1 then
let m ← m + i - T[i], i ← T[i]
else
let i ← 0, m ← m + 1
(来自维基百科)。用W
子串和S
要搜索的字符串,都是从零开始的数组。
if
我对算法中的最后一个子句有一个疑问:if T[i] > -1 then
基于T
-vector 构造算法,似乎只有T[i]
index 小于零i = 0
。在这种情况下,可以对索引执行更快的“检查”(数组访问是一条附加指令,特别是如果考虑到可能的缓存错误),就像 assignment 一样i ← 0
。
的构造T
由以下算法完成:
let pos ← 2, cmd ← 0
let T[0] ← -1, T[1] ← 0
while pos < length(W) do
if W[pos-1] = W[cnd] then
let cnd ← cnd + 1, T[pos] ← cnd, pos ← pos + 1
else if cnd > 0 then // (*)
let cnd ← T[cnd]
else
let T[pos] ← 0, pos ← pos + 1
(来自维基百科)。
现在可以看到算法只写入0
或cnd
to的值T
。对于第一种类型的赋值,这个陈述是平凡的。对于第二种情况,它取决于 的值cmd
。
现在唯一cmd
可以减少的方法是通过第二种情况(*)
,在这种情况下, cmd 将变得越来越小,直到它的值为零或更小。但由于cmd
从数组的已初始化部分获取值,因此可以是0
或-1
。如果cmd
确实是-1
,这将导致T[pos]
设置为,0
因为在设置值之前有一个增量。万一cmd
为零,则完全没有问题。
因此,更有效的算法将是:
let m ← 0, i ← 0
while m + i < length(S) do
if W[i] = S[m + i] then
if i = length(W) - 1 then
return m
let i ← i + 1
else
if i > 0 then
let m ← m + i - T[i], i ← T[i]
else
let m ← m + 1
这个说法正确吗?如果没有,你能给出一个子字符串,其中两个或多个-1
s 出现在T
-array 中吗?