当我了解Boyer-Moore Algorithm时,我正在Python中为子字符串搜索实现Galil Rule。我在网上浏览了加利尔规则,但我找不到任何更多的句子,我无法访问原始论文。如何将其实现到我当前的算法中?
i = 0
while i < (N - M + 1):
skip = 0
for j in reversed(range(0, M)):
if pattern[j] != text[i + j]:
skip = max(1, j - offsets[text[i+j]])
break
if skip == 0:
return i
i += skip
return -1
注意:
实施例: aaabcb
我发现的几句话已经说过跟踪我内循环中第一次不匹配的时间(j,如果内循环内的if语句是True)和我开始比较的位置(i + j,在我的情况下)。我理解直觉,我已经检查过它们之间的所有指数,所以我不应该再做那些比较。我只是不明白如何连接点并实现。
答案 0 :(得分:4)
Galil规则是利用模式中的周期性来减少比较。假设你有一个模式abcabcab
。它是周期性最小的句点abc
。通常,如果字符串P
使U
为P
的前缀,则模式UUUUU...
是周期性的。 (在上面的示例中,abcabcab
显然是重复字符串abc
= U
的前缀。)我们将最短的字符串称为P
的句点。假设该句点的长度为k
(在k = 3
之后的U = abc
中的示例中)。
首先,请记住,在文本中发现P
后,Galil规则仅应用 。当你这样做时,Galil规则说你可以移动k
(模式的周期性),你只需要比较现在移位模式的最后k
个字符,以确定是否存在一场比赛。
以下是一个例子:
P = ababa
T = bababababab
U = ab
k = 2
首次出现:b[ababa]babab
。现在你可以按k = 2
换班,你只需要检查模式的最后两个字符:
T = bababa[ba]bab
P = aba[ba] // Only need to compare chars inside brackets for next match.
P
的其余部分必须匹配,因为P是周期性的,您将其从现有匹配的时间段k
移开(这是至关重要)所以重复的部分将很好地排队。
如果您找到了另一场比赛,请重复一遍。但是,如果发现不匹配,则返回标准的Boyer-Moore算法,直到找到另一个匹配项。请记住,当您找到匹配和时,您只能使用Galil规则k
(否则无法保证模式与之前的匹配项对齐)。
现在,您可能想知道如何确定给定模式k
的{{1}}。您需要首先计算后缀数组P
,其中N
将是前缀N[i]
和P[0, i]
的最长公共后缀的长度。 (您可以使用Z算法计算P
的 reverse 上的前缀数组Z
来计算后缀数组,例如here所述。)获得后缀数组后,您可以轻松找到P
,因为它是最小的k
,k > 0
(其中N[m - k - 1] == m - k
)。
例如:
m = |P|
答案 1 :(得分:1)
@Lajos Nagy的回答完美地解释了Galil规则的思想,但是我们有一种更简单的方法来计算k
:
只需使用KMP算法的前缀功能。
prefix[i]
表示P[0..i]
的最长适当前缀,它也是后缀。
然后k = m-prefix[m-1]
。