Rabin-Karp 模板

一句话说明

Rabin-Karp 的核心是滑动窗口哈希:旧窗口删左端、整体乘基数、再加上新字符。

模板适用场景

  • 定长子串匹配
  • 子串判重
  • 需要快速比较很多相同长度子串

如果题目要求严格线性且不想碰哈希冲突,优先 KMP / Z。

Go 模板:查找所有匹配位置

func RabinKarpSearch(text, pattern string) []int {
    if len(pattern) == 0 {
        result := make([]int, len(text)+1)
        for i := range result {
            result[i] = i
        }
        return result
    }
    if len(pattern) > len(text) {
        return nil
    }
 
    const base = 256
    const mod = 1000000007
 
    width := len(pattern)
    highest := 1
    for i := 0; i < width-1; i++ {
        highest = highest * base % mod
    }
 
    patternHash, windowHash := 0, 0
    for i := 0; i < width; i++ {
        patternHash = (patternHash*base + int(pattern[i])) % mod
        windowHash = (windowHash*base + int(text[i])) % mod
    }
 
    positions := []int{}
    for left := 0; left+width <= len(text); left++ {
        if windowHash == patternHash && text[left:left+width] == pattern {
            positions = append(positions, left)
        }
 
        if left+width < len(text) {
            windowHash = (windowHash - int(text[left])*highest) % mod
            if windowHash < 0 {
                windowHash += mod
            }
            windowHash = (windowHash*base + int(text[left+width])) % mod
        }
    }
 
    return positions
}

为什么哈希相等后还要再比一次

因为哈希可能碰撞。
所以标准模板必须做:

  • 先比哈希
  • 哈希相等后再核对原串

最容易被忽略的一步

去掉窗口最高位字符后,哈希可能变成负数。
因此要及时做:

if windowHash < 0 {
    windowHash += mod
}

易错点

Rabin-Karp 模板最容易错的地方

  • 哈希相同不等于字符串一定相同,还要再次确认。
  • 去掉最高位贡献后要及时取模。
  • 如果非常在意碰撞率,可以上双哈希。

复杂度

场景时间复杂度
平均情况O(n + m)
最坏情况O(nm)

相关主题


返回:算法模板