使用 Rabin-Karp 搜索字符串中的多个模式

Posted

技术标签:

【中文标题】使用 Rabin-Karp 搜索字符串中的多个模式【英文标题】:Using Rabin-Karp to search for multiple patterns in a string 【发布时间】:2010-11-22 00:47:30 【问题描述】:

根据Rabin-Karp 字符串匹配算法上的wikipedia entry,它可以用来同时在一个字符串中寻找几个不同的模式,同时仍然保持线性复杂度。很明显,当所有模式的长度相同时,这很容易做到,但我仍然不明白如何在同时搜索不同长度的模式时保持 O(n) 复杂度。有人可以对此有所了解吗?

编辑(2011 年 12 月):

***文章已经更新,不再声称在 O(n) 中匹配多个不同长度的模式。

【问题讨论】:

这不是确切的答案,因为它只处理一次搜索一个字符串,而不是多个,但有一些可能有用的信息(在标题“Karp Rabin”下)可能会有所帮助你在:www-igm.univ-mlv.fr/~lecroq/string/index.html ***的文章声称它可以在 O(n) 时间内找到多种模式。 【参考方案1】:

我不确定这是否是正确的答案,但无论如何: 在构造哈希值时,我们可以检查字符串哈希集中的匹配项。也就是 current 哈希值。哈希函数/代码通常实现为一个循环,在该循环内我们可以插入快速查找。 当然,我们必须从字符串集中选择m 以获得最大字符串长度。 更新:来自***,

[...]
for i from 1 to n-m+1
         if hs ∈ hsubs
             if s[i..i+m-1] = a substring with hash hs
                 return i
         hs := hash(s[i+1..i+m]) // <---- calculating current hash
[...]

我们以m 步骤计算当前哈希。每一步都有一个 临时 哈希值,我们可以在哈希集中查找( O(1) 复杂度)。所有散列将具有相同的大小,即 32 位。

更新 2: 摊销(平均)O(n) 时间复杂度? 上面我说过m必须有最大字符串长度。事实证明,我们可以利用相反的情况。 使用 hashing for shifting substring search 和固定的 m 大小,我们可以实现 O(n) 复杂度。 如果我们有可变长度的字符串,我们可以将m 设置为最小字符串长度。此外,在散列集合中,我们不将散列与整个字符串相关联,而是与它的前 m 个字符相关联。 现在,在搜索文本时,我们检查当前哈希是否在哈希集中,并检查关联的字符串是否匹配。 这种技术会增加误报率,但平均而言它的时间复杂度为 O(n)。

【讨论】:

您能详细说明一下吗?据我所知,您建议保留多个哈希(每个模式长度一个)并使用它们来查询哈希表/BST。但是,如果每次迭代的散列使复杂度超过线性,计算的不是超过一个常数吗? 感谢您的解释。但这正是我困惑的根源。如果我们以 m 步计算当前哈希值,我们的整体复杂度就不再是线性的。它变成了O(n*m)(n是字符串的长度,m是最长模式的长度)。 @MAK, O(n) 是可行的,如果所有字符串都是m 大小并且我们按照使用散列进行移位子串搜索一节中提到的那样计算散列。但是对于可变长度字符串,IMO,O(nm) 是我们可以用 Rabin-Karp 算法做的最好的。也许那个 Wiki 条目不清楚。 我开始认为***条目在这方面是完全错误的。它声称复杂度应该是 O(n+k)。再次感谢。 @MAK,请参阅更新 2。如果我想出更好的解决方案,我会发布新的更新。【参考方案2】:

这是因为子字符串的哈希值在数学上是相关的。计算散列 H(S,j)(从字符串 S 的第 j 个位置开始的字符的散列)需要 O(m)长度为 m 的字符串的时间。但是一旦你有了这个,计算 H(S, j+1) 可以在恒定时间内完成,因为 H(S, j+1) 可以表示为H(S, j)的函数。

O(m) + O(1) => O(m),即线性时间。

Here's a link 对此进行了更详细的描述(例如,参见“是什么让 Rabin-Karp 快速?”部分)

【讨论】:

我明白为什么 Rabin-Karp 速度很快。我以前习惯于在字符串中查找单个模式。我试图弄清楚如何使用它在 O(n) 时间内同时在字符串中查找多个模式(如果您一个一个地搜索 k 个模式,则与 O(n*k) 相反)。 @MAK:对不起,我误解了你的问题。***文章底部的答案不是吗? “相比之下,上面的变体 Rabin-Karp 可以在 O(n+k) 时间内找到所有 k 模式,因为哈希表会在 O(1) 时间内检查子字符串哈希是否等于任何模式哈希。”创建哈希是 O(​​k)。在哈希表中查找匹配项是 O(1) 操作。如果有任何匹配,你就赢了。

以上是关于使用 Rabin-Karp 搜索字符串中的多个模式的主要内容,如果未能解决你的问题,请参考以下文章

Rabin-Karp 字符串搜索算法

使用 C# 在 LeetCode 28“implement strStr()”中实现 Rabin-Karp 算法来搜索字符串的问题

如何使用修改后的 Rabin-Karp 为字母分配数字以进行字谜搜索

Rabin-Karp指纹字符串查找算法

模式匹配:滚动哈希到 Rabin-Karp 算法

模式匹配:滚动哈希到 Rabin-Karp 算法