哈希算法题目:Rabin-Karp 字符串匹配算法
题目描述:
给定一个文本字符串 text 和一个模式字符串 pattern,请你实现 Rabin-Karp 算法,在 text 中查找所有 pattern 出现的位置。该算法通过哈希函数快速比较子串的哈希值,从而在平均情况下实现高效匹配。
解题过程循序渐进讲解
-
核心思想
Rabin-Karp 算法的核心是利用哈希函数计算模式串pattern的哈希值,以及文本串text中每个长度为m(m为pattern的长度)的子串的哈希值。如果某个子串的哈希值与pattern的哈希值相等,则进一步验证该子串是否与pattern完全匹配(避免哈希冲突导致的误判)。 -
哈希函数设计
为了高效计算滑动窗口中的子串哈希值,我们采用"滚动哈希"(Rolling Hash)机制。常用的一种哈希函数是多项式哈希:- 将字符串视为一个多进制数(例如基数为
base = 26用于小写字母),计算其数值模一个大素数prime的结果。 - 公式:对于字符串
s,哈希值
- 将字符串视为一个多进制数(例如基数为
\[ \text{hash}(s) = (s[0] \times base^{m-1} + s[1] \times base^{m-2} + \dots + s[m-1] \times base^0) \mod \text{prime} \]
- 选择大素数
prime是为了减少哈希冲突,例如10^9+7。
- 滚动哈希计算步骤
- 预处理:计算
pattern的哈希值hash_pattern。 - 初始化:计算
text第一个长度为m的子串text[0:m]的哈希值hash_window。 - 滑动窗口:当窗口从
i移动到i+1时,旧字符text[i]离开,新字符text[i+m]进入。新哈希值可通过以下公式快速计算:
- 预处理:计算
\[ \text{hash\_new} = (\text{hash\_old} - \text{text[i]} \times base^{m-1}) \times base + \text{text[i+m]} \mod \text{prime} \]
注意:减法后需处理负数(加 `prime` 再取模)。
-
匹配验证
- 若
hash_window == hash_pattern,则逐字符比较text[i:i+m]与pattern,确认是否完全匹配。 - 匹配成功则记录起始位置
i。
- 若
-
复杂度分析
- 平均时间复杂度:O(n + m),其中 n 为
text长度。 - 最坏情况(大量哈希冲突):O(n × m),但通过合理选择
base和prime可避免。
- 平均时间复杂度:O(n + m),其中 n 为
示例演示
设 text = "abracadabra",pattern = "abra",base = 26,prime = 10^9+7。
- 计算
pattern哈希值。 - 滑动窗口依次计算
"abra"、"brac"、"raca"… 的哈希值,与pattern比较。 - 在位置 0 和 7 找到匹配。
通过滚动哈希,Rabin-Karp 避免了每次重新计算子串哈希值,显著提升了效率。