RabinKarp 哈希算法、二分查找:求解《1044. 最长重复子串》

2022-04-22 05:28:50

用 RabinKarp 哈希算法和二分查找,求解《1044. 最长重复子串》

《1044.最长重复子串》哈希相减的步骤示意图

Rabin Karp 哈希算法

取余后,哈希相减为负

取余后,哈希冲突

例题

1044. 最长重复子串

给你一个字符串 s ,考虑其所有 重复子串 :即 s 的(连续)子串,在 s 中出现 2 次或更多次。这些出现之间可能存在重叠。 返回 任意一个 可能具有最长长度的重复子串。如果 s 不含重复子串,那么答案为 "" 。

答案

Rabin Karp 哈希算法 · 二分查找

枚举重复字符串长度,用 Rabin Karp 判断该长度下有无字符串重复

var longestDupSubstring = function(s) {
  const n = s.length, hash = new Uint32Array(n), base = new Uint32Array(n), BASE = 26, MOD = 1e8 + 9
  base[0] = 1, hash[0] = s.charCodeAt(0) - 96
  for (let i = 1; i < n; i++) {
    base[i] = base[i - 1] * BASE % MOD
    hash[i] = (hash[i - 1] * BASE + s.charCodeAt(i) - 96) % MOD
  }
  const rabinHash = len => {
    const map = new Map()
    for (let i = 0; i + len - 1 < n; i++) {
      const h = (hash[i + len - 1] + MOD - (i > 0 ? hash[i - 1] * base[len] : 0) % MOD) % MOD
      if (map.has(h)) {
        const iPre = map.get(h), t = s.substring(i, i + len)
        if (t === s.substring(iPre, iPre + len)) return t
      }
      map.set(h, i)
    }
    return ''
  }
  let l = 0, r = n - 1, t = ans = ''
  while (l < r) {
    const m = l + r + 1 >> 1
    if (t = rabinHash(m)) {
      l = m
      if (t.length > ans.length) ans = t
    } else r = m - 1
  }
  return ans
};
func longestDupSubstring(s string) string {
  n := len(s)
  hash, base, BASE, MOD := make([]int, n), make([]int, n), 26, 100000007
  base[0] = 1
  hash[0] = int(s[0]) - 96
  for i := 1; i < n; i++ {
    base[i] = base[i - 1] * BASE % MOD
    hash[i] = (hash[i - 1] * BASE + int(s[i]) - 96) % MOD
  }
  var rabinHash func (len int) string
  rabinHash = func (len int) string {
    set := make(map[int]int)
    for i := 0; i + len - 1 < n; i++ {
      h := 0
      if (i == 0) {
        h = hash[i + len - 1]
      } else {
        h = hash[i + len - 1] + MOD - hash[i - 1] * base[len] % MOD
      }
      h %= MOD
      if iPre, ok := set[h]; ok {
        t := s[iPre : iPre + len]
        if t == s[i : i + len] {
          return t
        } 
      }
      set[h] = i
    }
    return ""
  }
  l, r, ans := 0, len(s) - 1, ""
  for l < r {
    m := (l + r + 1) >> 1
    t := rabinHash(m)
    if t != "" {
      l = m
      if len(t) > len(ans) {
        ans = t
      }
    } else {
      r = m - 1
    }
  }
  return ans
}
自定义排序:求解《937. 重新排列日志文件》
自定义排序和 Golang 的字符串分割函数,求解《937. 重新排列日志文件》
消元法,正则和栈:求解《591. 标签验证器》
消元法,正则和栈,求解《591. 标签验证器》
RabinKarp 哈希算法:求解《796. 旋转字符串》《459. 重复的子字符串》和《1316. 不同的循环子字符串》
RabinKarp 哈希算法,求解《796. 旋转字符串》《459. 重复的子字符串》和《1316. 不同的循环子字符串》