《字符串哈希》基础概念

一、算法概述

  1. 定义:字符串哈希是一种将字符串转换为数值(哈希值)的技术,通过哈希值快速比较字符串的相等性或查找子串。其核心是利用基数映射和前缀哈希数组,实现子串哈希值的O(1)时间查询。
  2. 核心思想:将字符串视为基数为B的数,通过预处理前缀哈希数组和幂次数组,将子串的哈希计算转化为数值运算,从而高效判断子串是否相同。
  3. 应用场景
    • 字符串匹配(如KMP、Boyer-Moore算法的优化)
    • 重复子串检测
    • 字符串哈希表构建

二、算法思路

  1. 基数与幂次
    • 选择一个基数B(如26、271、1e9+7等),将字符映射为数值(如a=1,b=2,…)。
    • 预处理幂次数组Power[i] = B^i,用于快速计算哈希值的权重。
  2. 前缀哈希数组
    • 定义Hash[i]为字符串前i个字符的哈希值,计算公式为:
      Hash[i] = Hash[i-1] * B + s[i]
      其中s[i]为第i个字符的数值映射。
  3. 子串哈希计算
    • 子串s[l...r]的哈希值为:
      get(l, r) = Hash[r] - Hash[l-1] * Power[r-l+1]
      该公式通过前缀哈希和幂次抵消前l-1个字符的影响,得到子串的独立哈希值。

三、伪代码实现

1. 数据结构与全局变量

常量 maxn = 1000010  # 最大字符串长度
类型 ull = 无符号长整型  # 用于存储哈希值,避免溢出
常量 B = 271  # 哈希基数
数组 Power[maxn]  # 幂次数组,Power[i] = B^i
数组 Hash[maxn]  # 前缀哈希数组

2. 核心函数框架

算法:字符串哈希模板
输入:字符串 s
输出:支持快速查询子串哈希值的函数

函数 init(s):
    # 初始化前缀哈希数组和幂次数组
    Power[0] = 1  # B^0 = 1
    Hash[0] = s[0]的数值映射  # 第一个字符的哈希值
    for i from 1 to s.length()-1:
        Hash[i] = Hash[i-1] * B + s[i]的数值映射  # 前缀哈希计算
        Power[i] = Power[i-1] * B  # 幂次计算

函数 get(l, r):
    # 计算子串s[l...r]的哈希值(l, r从0开始索引)
    if l == 0:
        return Hash[r]  # 从字符串开头开始的子串
    return Hash[r] - Hash[l-1] * Power[r - l + 1]  # 子串哈希公式

# 主程序(示例)
输入字符串 s
init(s)
输入查询次数 q
for each query:
    输入 l, r
    输出 get(l, r)

四、算法解释

1. 初始化过程

  • 幂次数组Power[i]存储基数B的i次幂,用于后续计算哈希值时的权重调整。例如,Power[3] = B³表示第三位字符的权重。
  • 前缀哈希数组Hash[i]表示前i+1个字符的哈希值(假设索引从0开始)。例如,字符串"abc"的Hash[0] = 'a'Hash[1] = 'a'*B + 'b'Hash[2] = ('a'*B + 'b')*B + 'c' = 'a'*B² + 'b'*B + 'c'

2. 子串哈希计算原理

  • 对于子串s[l...r],其哈希值等价于完整哈希值Hash[r]减去前l个字符的哈希值Hash[l-1]乘以权重Power[r-l+1]
  • 数学推导:
    假设字符串为s[0...n-1],则:
    Hash[r] = s[0]*B^r + s[1]*B^(r-1) + ... + s[r]
    Hash[l-1] = s[0]*B^(l-1) + s[1]*B^(l-2) + ... + s[l-1]
    Hash[l-1] * Power[r-l+1] = s[0]*B^r + s[1]*B^(r-1) + ... + s[l-1]*B^(r-l+1)
    因此,Hash[r] - Hash[l-1] * Power[r-l+1] = s[l]*B^(r-l) + ... + s[r],即子串s[l...r]的哈希值。

3. 示例演示

  • 字符串:“abc”,基数B=10,字符映射为a=1, b=2, c=3
  • 初始化:
    Power[0]=1, Power[1]=10, Power[2]=100
    Hash[0]=1, Hash[1]=1*10+2=12, Hash[2]=12*10+3=123
  • 计算子串"bc"(l=1, r=2)的哈希值:
    get(1, 2) = Hash[2] - Hash[0] * Power[2-1+1] = 123 - 1 * 100 = 23,对应数值"23",即子串"bc"的哈希值。

4. 哈希冲突处理

  • 双哈希策略:使用两个不同的基数(如B1=271,B2=911382629)和模数(如1e18+3,1e18+7)计算双重哈希值,降低冲突概率。
  • 模数选择:选择大质数(如1e9+7、1e18+3)作为模数,减少哈希碰撞的可能性。

五、复杂度分析

  1. 时间复杂度
    • 初始化:O(n),预处理前缀哈希数组和幂次数组。
    • 单次查询:O(1),直接通过公式计算子串哈希值。
    • 总时间复杂度:O(n + q),其中q为查询次数。
  2. 空间复杂度
    • 前缀哈希数组Hash:O(n)。
    • 幂次数组Power:O(n)。
    • 总空间复杂度:O(n)。
  3. 优化点
    • 预处理幂次数组时可预先计算到最大可能长度,避免重复计算。
    • 对于长字符串,使用unsigned long long自然溢出(模2^64)代替显式模数,减少计算开销。
  4. 适用范围
    • 适用于需要频繁查询子串相等性的场景,如字符串匹配、重复子串检测等,时间效率优于直接比较字符串。

  本文为作者(英雄哪里出来)在抖音的独家课程《英雄C++入门到精通》、《英雄C语言入门到精通》、《英雄Python入门到精通》三个课程的配套文字讲解,如需了解算法视频课程,请移步 作者本人 的抖音直播间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英雄哪里出来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值