Skip to content

Commit

Permalink
Update 02.String-Rabin-Karp.md
Browse files Browse the repository at this point in the history
  • Loading branch information
itcharge committed Jan 11, 2024
1 parent fd1e206 commit f15eb48
Showing 1 changed file with 6 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
2. 通过滚动哈希算法求出模式串 $p$ 的哈希值 $hash\underline{}p$。
3. 再通过滚动哈希算法对文本串 $T$ 中 $n - m + 1$ 个子串分别求哈希值 $hash\underline{}t$。
4. 然后逐个与模式串的哈希值比较大小。
1. 如果当前子串的哈希值 $hash\underline{}t$ 与模式串的哈希值 $hash_p$ 不同,则说明两者不匹配,则继续向后匹配。
2. 如果当前子串的哈希值 $hash\underline{}t$ 与模式串的哈希值 $hash_p$ 相等,则验证当前子串和模式串的每个字符是否真的相等(避免哈希冲突)。
1. 如果当前子串的哈希值 $hash\underline{}t$ 与模式串的哈希值 $hash\underline{}p$ 不同,则说明两者不匹配,则继续向后匹配。
2. 如果当前子串的哈希值 $hash\underline{}t$ 与模式串的哈希值 $hash\underline{}p$ 相等,则验证当前子串和模式串的每个字符是否真的相等(避免哈希冲突)。
1. 如果当前子串和模式串的每个字符相等,则说明当前子串和模式串匹配。
2. 如果当前子串和模式串的每个字符不相等,则说明两者不匹配,继续向后匹配。
5. 比较到末尾,如果仍未成功匹配,则说明文本串 $T$ 中不包含模式串 $p$,方法返回 $-1$。
Expand All @@ -32,17 +32,17 @@ RK 算法中的滚动哈希算法主要是利用了 **「Rabin fingerprint 思

比如 `"cat"` 的哈希值就可以表示为:

$\begin{align} Hash(cat) &= c \times 26 \times 26 + a \times 26 + t \times 1 \cr &= 2 \times 26 \times 26 + 0 \times 26 + 19 \times 1 \cr &= 1371 \end{align}$
$\begin{aligned} Hash(cat) &= c \times 26 \times 26 + a \times 26 + t \times 1 \cr &= 2 \times 26 \times 26 + 0 \times 26 + 19 \times 1 \cr &= 1371 \end{aligned}$

这种按位计算哈希值的哈希函数有一个特点:在计算相邻子串时,可以利用上一个子串的哈希值。

比如说 $cat$ 的相邻子串为 `"ate"`。按照刚才哈希函数计算,可以得出 `"ate"` 的哈希值为:

$\begin{align} Hash(ate) &= a \times 26 \times 26 + t \times 26 + e \times 1 \cr &= 0 \times 26 \times 26 + 19 \times 26 + 4 \times 1 \cr &= 498 \end{align}$
$\begin{aligned} Hash(ate) &= a \times 26 \times 26 + t \times 26 + e \times 1 \cr &= 0 \times 26 \times 26 + 19 \times 26 + 4 \times 1 \cr &= 498 \end{aligned}$

如果利用上一个子串 `"cat"` 的哈希值计算 `"ate"`,则 `"ate"` 的哈希值为:

$\begin{align} Hash(ate) &= (Hash(cat) - c \times 26 \times 26) * 26 + e \times 26 \cr &= (1371 - 2 \times 26 \times 26) \times 26 + 4 \times 1 \cr &= 498 \end{align}$
$\begin{aligned} Hash(ate) &= (Hash(cat) - c \times 26 \times 26) * 26 + e \times 26 \cr &= (1371 - 2 \times 26 \times 26) \times 26 + 4 \times 1 \cr &= 498 \end{aligned}$

可以看出,这两种方式计算出的哈希值是相同的。但是第二种计算方式不需要再遍历子串,只需要进行一位字符的计算即可得出整个子串的哈希值。这样每次计算子串哈希值的时间复杂度就降到了 $O(1)$。然后我们就可以通过滚动哈希算法快速计算出子串的哈希值了。

Expand Down Expand Up @@ -94,7 +94,7 @@ def rabinKarp(T: str, p: str, d, q) -> int:

RK 算法可以看做是 BF 算法的一种改进。在 BF 算法中,每一个字符都需要进行比较。而在 RK 算法中,判断模式串的哈希值与每个子串的哈希值之间是否相等的时间复杂度为 $O(1)$。总共需要比较 $n - m + 1$ 个子串的哈希值,所以 RK 算法的整体时间复杂度为 $O(n)$。跟 BF 算法相比,RK 算法的效率提高了很多。

但是如果存在冲突的情况下,算法的效率会降低。最坏情况是每一次比较模式串的哈希值和子串的哈希值时都相等,但是每一次都会出现冲突,那么每一次都需要验证模式串和子串每个字符是否完全相同,那么总的比较次数就是 $m \times (n - m + 1) $,时间复杂度就会退化为 $O(m \times n)$。
但是如果存在冲突的情况下,算法的效率会降低。最坏情况是每一次比较模式串的哈希值和子串的哈希值时都相等,但是每一次都会出现冲突,那么每一次都需要验证模式串和子串每个字符是否完全相同,那么总的比较次数就是 $m \times (n - m + 1)$,时间复杂度就会退化为 $O(m \times n)$。

## 参考资料

Expand Down

0 comments on commit f15eb48

Please sign in to comment.