Algorithm Knuth-Morris-Pratt算法中的冗余指令
目的是查找字符串中第一个(可能是下一个)出现的子字符串。由于子字符串可以包含重复部分,因此它使用某种回溯机制。这是伪代码中的算法:Algorithm Knuth-Morris-Pratt算法中的冗余指令,algorithm,optimization,knuth-morris-pratt,Algorithm,Optimization,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
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
(摘自维基百科)
现在我们可以看到,该算法只将0
或cnd
的值写入T
。对于第一种类型的赋值,该语句非常正确。对于第二种情况,它取决于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
让m← 0,我← 0
而m+i<长度
如果W[i]=S[m+i],那么
如果i=长度(W)-1,则
返回m
让我← i+1
其他的
如果i>0,那么
让我← m+i-T[i],i← T[i]
其他的
让我← m+1
这句话对吗?如果没有,您能给出一个子字符串,其中两个或多个
-1
出现在T
-array中吗?这对我来说很好,尽管我不知道在实践中会有多大不同。当然,在常见场景中,大多数循环都是在i
为0且字符位于s[m]
≠ <代码>W[0]
我不认为维基百科中的算法是“官方的”或超优化的;这是为了说教
当遇到无法扩展任何候选匹配的字符且不是所搜索单词的第一个字符时,出现if
的第二个分支;在这种情况下,有必要移动该角色。(这是前面提到的常见情况。)
在输入故障分支的所有其他情况下,m+i
不变。在成功案例和最终失败案例中,m+i
正好增加1
由于min
和max
在许多CPU上都是无分支操作码,另一种优化方法是将T[0]
设置为0
,而不是-1
,并将循环更改为:
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
let m ← m + max(1, i - T[i]), i ← T[i]
然而,作者抱怨上述简单算法效率低下,并花了几页探讨优化
毫无疑问,这是不必要的低效吗?
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
let m ← m + max(1, i - T[i]), i ← T[i]
(* Note: here, m is the length of pattern and n is the length of the input *)
j := k := 1;
while j ≤ m and k ≤ n do
begin
while j > 0 and text[k] ≠ pattern[j]
do j := next[j];
k := k + l; j := j + l;
end;