[英]string matching with finite automata
我正在阅读Cormen的书“算法导论”中的字符串算法。 对于过渡,如下所示。
我的问题:为什么我们要做min(m+1, q+2)
,为什么我们要m递增1,q递增2。
以下链接有上述问题。
http://people.scs.carleton.ca/~maheshwa/courses/5703COMP/Fall2009/StringMatching.pdf
请帮助一个简单的例子。
Algorithm Compute-Transition-Function(P, Sigma)
m = length(P);
for q = 0 through m do
for each character x in Sigma
k = min(m+1, q+2);
repeat k = k-1 // work backwards from q+1
until Pk 'is-suffix-of' Pqx;
d(q, x) = k; // assign transition table
end for;
end for;
return d;
End algorithm.
m + 1
因为在下一个repeat
循环中, k
首先减小。 q + 2
因为在repeat
你开始然后用q + 1
所以至少有1个字符。
以下代码可能存在边界问题(q == m丢失),但希望使索引更清晰。
m = length(P);
for q = 0 through m - 1 do // Loop through substrings [0, q+1]
for each character x in Sigma
k = q+1;
// work backwards from q+1
while not Pk 'is-suffix-of' Pqx;
do k = k-1; end do;
d(q, x) = k; // assign transition table
end for;
end for;
return d;
代码已被解释,所以这里是一个例子来看看发生了什么
假设字符串是nano
所以我们希望我们的状态与模式部分匹配。 与"nano"
部分匹配可能是"", "n", "na", "nan", or (the complete match) "nano"
本身。 换句话说,它们只是字符串的前缀。 一般来说,如果模式有m个字符,我们需要m + 1个状态; 这里m = 4,有五个州。
如果我们刚看到"...nan"
,看到另一个字符"x"
,我们应该去哪个州? 显然,如果x是匹配中的下一个字符(这里是“o”),我们应该转到下一个更长的前缀(这里是“nano”)。 很明显,一旦我们看到完全匹配,我们就会保持这种状态。 但是假设我们看到一个不同的角色,比如"a"
? 这意味着到目前为止,字符串看起来像"...nana"
。 我们可能会遇到的最长的部分匹配只是"na"
,即我们可以利用最后2个字符。 因此,从状态"nan"
,我们应绘制一个标记为"a"
的箭头,将状态为"na"
。 请注意, "na"
是"nano"
(因此是状态)的前缀,并且是"nana"
的后缀(因此是与我们刚刚看到的部分匹配的部分)。
通常,从状态+字符到状态的过渡是最长的字符串,它同时是原始模式的前缀和我们刚刚看到的状态+字符的后缀。 这足以告诉我们所有转换应该是什么。 如果我们正在寻找模式"nano"
,那么过渡表就是
n a o other
--- --- --- ---
empty: "n" empty empty empty
"n": "n" "na" empty empty
"na": "nan" empty empty empty
"nan": "n" "na" "nano" empty //just as an illustration, nan + n = n because we can only use the last 'n', nan + a = na because now we can use the last two 'na'
"nano": "nano" "nano" "nano" "nano"
那么现在我们如何使用这个表来实际进行模式搜索?
在字符串"banananona"
上模拟这个,我们得到状态序列为空,空, "n", "na", "nan", "na", "nan", "nano", "nano", "nano"
通过一次移动一个角色。 由于我们以状态"nano"
结束,因此该字符串在某处包含"nano"
。 所以让我们来看看最新情况以及如何使用上面的表格,在'b',我们没有任何可能的状态'n', 'na', 'nan', 'nano'
。 所以它算得空了......就像我们到了'ba'
。 当我们打到下一个字符'n'
,基本上是从空到n,因此我们使用上面的表,并看到它以'n'
结尾。 现在我们得到了banananona的4个角色,所以我们从'n'
开始添加...再次我们使用该表并看到它最终处于'na'
状态,依此类推......
转换表中的条目d(q,x)
包含消耗字符x
后模式的最长匹配前缀的长度,如果在消耗x
之前,最长匹配前缀是q
字符长。 因为我们消耗一个字母,所以它不能大于q+1
,并且由于该模式具有长度m
,所以它也可以最多为m
。 内循环repeat k = k-1 until condition(k)
,因此在测试任何东西之前, k
都会递减,因此k
必须以大于最大可能结果的1开始, k = min(m,q+1) + 1
。 如果内部循环是while negated_condition(k) { k = k-1; }
while negated_condition(k) { k = k-1; }
,将从k = min(m,q+1)
。
注意,通过将边界表用于Knuth-Morris-Pratt算法,可以更高效地计算转换表。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.