Rabin-Karp算法在滚动哈希中的应用:重复子串查找
字数 771 2025-11-03 00:20:06

Rabin-Karp算法在滚动哈希中的应用:重复子串查找

题目描述:给定一个字符串s,找出其中至少出现两次的最长子串。如果有多个这样的子串,返回任意一个即可。要求使用Rabin-Karp算法通过滚动哈希高效解决。

解题过程:

  1. 问题分析
  • 我们需要找到字符串s中重复出现的最长子串
  • 暴力解法需要枚举所有子串O(n²),然后比较每个子串是否重复出现O(n),总复杂度O(n³)
  • 使用哈希表存储子串可以优化到O(n²),但空间复杂度较高
  • Rabin-Karp算法通过滚动哈希可以在O(1)时间内计算子串哈希值
  1. Rabin-Karp滚动哈希原理
  • 将字符串视为基数为BASE的多项式:s[0]×BASE^(L-1) + s[1]×BASE^(L-2) + ... + s[L-1]×BASE^0
  • 使用模运算防止溢出:MOD取一个大质数
  • 关键技巧:当窗口向右滑动时,新哈希值 = (旧哈希值 - s[i]×BASE^(L-1))×BASE + s[i+L]
  1. 二分查找优化
  • 最长重复子串长度满足单调性:如果长度L存在重复子串,那么更短的子串也一定存在
  • 使用二分查找确定最大长度:left = 0, right = n-1
  • 对每个候选长度mid,检查是否存在重复子串
  1. 具体实现步骤
function longestRepeatingSubstring(s):
    n = len(s)
    left, right = 0, n-1
    result = ""
    
    while left <= right:
        mid = (left + right) // 2
        found = checkLength(s, mid)
        
        if found != "":
            result = found
            left = mid + 1  # 尝试更长的子串
        else:
            right = mid - 1  # 缩短子串长度
    
    return result

function checkLength(s, L):
    if L == 0: return ""
    
    BASE = 26  # 小写字母基数
    MOD = 10**9 + 7  # 大质数模数
    n = len(s)
    
    # 计算BASE^L % MOD
    power = 1
    for i in range(L-1):
        power = (power * BASE) % MOD
    
    # 计算第一个长度为L的子串哈希值
    hash_val = 0
    for i in range(L):
        hash_val = (hash_val * BASE + (ord(s[i]) - ord('a'))) % MOD
    
    seen = {hash_val: 0}  # 存储哈希值和起始位置
    
    # 滚动哈希检查所有长度为L的子串
    for i in range(1, n - L + 1):
        # 移除最左边的字符,添加最右边的字符
        hash_val = (hash_val - (ord(s[i-1]) - ord('a')) * power) % MOD
        hash_val = (hash_val * BASE + (ord(s[i+L-1]) - ord('a'))) % MOD
        
        # 处理负数
        if hash_val < 0:
            hash_val += MOD
        
        # 检查是否重复
        if hash_val in seen:
            # 验证是否真正匹配(防止哈希冲突)
            start = seen[hash_val]
            if s[start:start+L] == s[i:i+L]:
                return s[i:i+L]
        else:
            seen[hash_val] = i
    
    return ""
  1. 复杂度分析
  • 时间复杂度:O(n log n),二分查找O(log n)次,每次检查O(n)
  • 空间复杂度:O(n),存储哈希值和位置映射
  • 相比暴力解法的O(n³)有显著优化
  1. 哈希冲突处理
  • 虽然概率很低,但不同字符串可能产生相同哈希值
  • 当哈希值匹配时,需要实际比较字符串内容确认
  • 可以使用双重哈希(两个不同的MOD)进一步降低冲突概率

这个算法展示了滚动哈希在字符串匹配中的高效应用,通过巧妙的哈希计算和滑动窗口技术,将复杂度从O(n³)优化到O(n log n)。

Rabin-Karp算法在滚动哈希中的应用:重复子串查找 题目描述:给定一个字符串s,找出其中至少出现两次的最长子串。如果有多个这样的子串,返回任意一个即可。要求使用Rabin-Karp算法通过滚动哈希高效解决。 解题过程: 问题分析 我们需要找到字符串s中重复出现的最长子串 暴力解法需要枚举所有子串O(n²),然后比较每个子串是否重复出现O(n),总复杂度O(n³) 使用哈希表存储子串可以优化到O(n²),但空间复杂度较高 Rabin-Karp算法通过滚动哈希可以在O(1)时间内计算子串哈希值 Rabin-Karp滚动哈希原理 将字符串视为基数为BASE的多项式:s[ 0]×BASE^(L-1) + s[ 1]×BASE^(L-2) + ... + s[ L-1 ]×BASE^0 使用模运算防止溢出:MOD取一个大质数 关键技巧:当窗口向右滑动时,新哈希值 = (旧哈希值 - s[ i]×BASE^(L-1))×BASE + s[ i+L ] 二分查找优化 最长重复子串长度满足单调性:如果长度L存在重复子串,那么更短的子串也一定存在 使用二分查找确定最大长度:left = 0, right = n-1 对每个候选长度mid,检查是否存在重复子串 具体实现步骤 复杂度分析 时间复杂度:O(n log n),二分查找O(log n)次,每次检查O(n) 空间复杂度:O(n),存储哈希值和位置映射 相比暴力解法的O(n³)有显著优化 哈希冲突处理 虽然概率很低,但不同字符串可能产生相同哈希值 当哈希值匹配时,需要实际比较字符串内容确认 可以使用双重哈希(两个不同的MOD)进一步降低冲突概率 这个算法展示了滚动哈希在字符串匹配中的高效应用,通过巧妙的哈希计算和滑动窗口技术,将复杂度从O(n³)优化到O(n log n)。