如果我在前两个索引处使用具有相同字符的模式,F sharp KMP 算法将卡在第一个 while 循环中
F sharp KMP Algorithm is stuck in the first while loop if i use a pattern with the same characters at the first two indces
我正在研究 f sharp 中的 KMP 算法。虽然它适用于像 "ATAT" 这样的模式(结果将是 [|0; 0; 1; 2;|]),但当字符串的前 2 个字符相同且第 3 个字符相同时,第一个 while 循环进入死锁是另一个,例如"AAT"。
我明白为什么:首先,i 递增到 1。现在 while 循环的第一个条件为真,而第二个条件也为真,因为 "A" <> "T"。现在它将 i 设置为 prefixtable.[!i],这又是 1,我们开始吧。
你们能告诉我如何解决这个问题吗?
let kMPrefix (pattern : string) =
let (m : int) = pattern.Length - 1
let prefixTable = Array.create pattern.Length 0
// i : longest proper prefix that is also a suffix
let i = ref 0
// j: the index of the pattern for which the prefix value will be calculated
// starts with 1 because the first prefix value is always 0
for j in 1 .. m do
while !i > 0 && pattern.[!i] <> pattern.[j] do
i := prefixTable.[!i]
if pattern.[!i] = pattern.[j] then
i := !i+1
Array.set prefixTable j !i
prefixTable
我不确定如何通过小的修改来修复代码,因为它与 KMP 算法的查找 table 内容不匹配(至少是我在维基百科上找到的内容),这是:
- -1 对于索引 0
- 否则,当前位置之前匹配开头的连续元素的计数(不包括开头本身)
因此,我希望 "ATAT"
的输出是 [|-1; 0; 0; 1|]
,而不是 [|0; 0; 1; 2;|]
。
这种类型的问题可能更适合用函数式的方式来推理。要创建 KMP table,您可以使用递归函数逐个填充 table,跟踪最近有多少个字符匹配开头,并在第二个开始 运行字符索引。
可能的实现:
let buildKmpPrefixTable (pattern : string) =
let prefixTable = Array.zeroCreate pattern.Length
let rec run startIndex matchCount =
let writeIndex = startIndex + matchCount
if writeIndex < pattern.Length then
if pattern.[writeIndex] = pattern.[matchCount] then
prefixTable.[writeIndex] <- matchCount
run startIndex (matchCount + 1)
else
prefixTable.[writeIndex] <- matchCount
run (writeIndex + 1) 0
run 1 0
if pattern.Length > 0 then prefixTable.[0] <- -1
prefixTable
这种方法没有任何无穷无尽 loops/recursion 的危险,因为 run
的所有代码路径在下一次迭代中增加 writeIndex
或完成迭代。
术语注意事项:您在问题中描述的错误是无限循环,或者更一般地说,是非终止迭代。死锁特指这样一种情况,一个线程等待一个永远不会被释放的锁,因为持有它的线程自己也在等待一个永远不会被释放的锁,同样的原因。
我正在研究 f sharp 中的 KMP 算法。虽然它适用于像 "ATAT" 这样的模式(结果将是 [|0; 0; 1; 2;|]),但当字符串的前 2 个字符相同且第 3 个字符相同时,第一个 while 循环进入死锁是另一个,例如"AAT"。
我明白为什么:首先,i 递增到 1。现在 while 循环的第一个条件为真,而第二个条件也为真,因为 "A" <> "T"。现在它将 i 设置为 prefixtable.[!i],这又是 1,我们开始吧。
你们能告诉我如何解决这个问题吗?
let kMPrefix (pattern : string) =
let (m : int) = pattern.Length - 1
let prefixTable = Array.create pattern.Length 0
// i : longest proper prefix that is also a suffix
let i = ref 0
// j: the index of the pattern for which the prefix value will be calculated
// starts with 1 because the first prefix value is always 0
for j in 1 .. m do
while !i > 0 && pattern.[!i] <> pattern.[j] do
i := prefixTable.[!i]
if pattern.[!i] = pattern.[j] then
i := !i+1
Array.set prefixTable j !i
prefixTable
我不确定如何通过小的修改来修复代码,因为它与 KMP 算法的查找 table 内容不匹配(至少是我在维基百科上找到的内容),这是:
- -1 对于索引 0
- 否则,当前位置之前匹配开头的连续元素的计数(不包括开头本身)
因此,我希望 "ATAT"
的输出是 [|-1; 0; 0; 1|]
,而不是 [|0; 0; 1; 2;|]
。
这种类型的问题可能更适合用函数式的方式来推理。要创建 KMP table,您可以使用递归函数逐个填充 table,跟踪最近有多少个字符匹配开头,并在第二个开始 运行字符索引。
可能的实现:
let buildKmpPrefixTable (pattern : string) =
let prefixTable = Array.zeroCreate pattern.Length
let rec run startIndex matchCount =
let writeIndex = startIndex + matchCount
if writeIndex < pattern.Length then
if pattern.[writeIndex] = pattern.[matchCount] then
prefixTable.[writeIndex] <- matchCount
run startIndex (matchCount + 1)
else
prefixTable.[writeIndex] <- matchCount
run (writeIndex + 1) 0
run 1 0
if pattern.Length > 0 then prefixTable.[0] <- -1
prefixTable
这种方法没有任何无穷无尽 loops/recursion 的危险,因为 run
的所有代码路径在下一次迭代中增加 writeIndex
或完成迭代。
术语注意事项:您在问题中描述的错误是无限循环,或者更一般地说,是非终止迭代。死锁特指这样一种情况,一个线程等待一个永远不会被释放的锁,因为持有它的线程自己也在等待一个永远不会被释放的锁,同样的原因。