字符串问题的江湖奇宝:进制哈希

news/2025/3/26 0:33:38/文章来源:https://www.cnblogs.com/ofnoname/p/18792549

江湖中,剑客以快制胜,而算法竞赛里,字符串哈希(String Hashing)便是那柄出招如电的快剑。

各种字符串问题纷乱复杂,各种字符串算法招式繁复,需苦练内功心法。但字符串哈希算法却只凭一招:将字符串化作数字,以数论为刃,至简之道斩尽来犯之敌

但此招并非无懈可击。若遇精心构造的数据,它可能一剑刺空,露出破绽。然而,在绝大多数情况,它仍是侠客们最趁手的兵器——七分准,三分险,却快得让人无从招架

何为进制哈希?

哈希是复杂对象到较小整数的映射,而进制版本的字符串哈希是一种很有趣的哈希方式。他把字符串转化为一个b进制的数值

例如,字符串 "ab"

  • 若设 u=1, v=2,取 base=131(进制基数),则其哈希值为:

    \[H('uv') = 1 \times 131^1 + 2 \times 131^0 = 133 \]

既然是将字符视为数,那每个字符需要一个值,同时还需要一个进制大小。

考虑字符集(Character Set),一般就使用字符的编码即可(如 ascii 或 unicode),简单起见本文只考虑小写字母集:a-z(26个字符),对应0-25

考虑基数(Base)的选择,基数显然至少等于字符集大小。若用a-z(26字符),则基数至少取26,实际上也可以更大,常用质数基数:131、13331 等。

随着字符串长度增加,这个数迅速就变得非常大,所以要再对某个大质数取模,便得到最终的编码。

查询子串哈希

字符串哈希之所以是"快剑",因为它有一个奇妙的性质:它能通过前缀哈希(Prefix Hash),在 O(1) 时间内斩出任意子串的哈希值。

选定编码和基数后,我们处理出a[i]:存储前i个字符的哈希值,显然有秦九韶算法:a[i] = a[i-1] * base + d;

现在我们要计算子串s[l..r]的哈希,其原理如同在进制数中取数:

  1. 将前缀哈希a[r]视为大数。
  2. 减去a[l-1]向左位移(r-l+1)位的干扰(通过乘p[r-l+1]实现,p为基数的幂次)

image

// 为了简单和效率,直接选用 unsigned long long,模数等价于2^64,称为自然溢出。
class hashstr {using u64 = unsigned long long;int n;u64 mod;vector<u64> a, p;const static int base = 131;
public:hashstr(const string &s, u64 mod = 0): n(s.size()), a(n), p(n+1) {u64 x = 0;for (int i = 0; i < n; ++i) {int d = s[i] - 'a';a[i] = x * base + d;}p[0] = 1;for (int i = 1; i <= n; ++i) p[i] = p[i-1] * base;}u64 hash(int l, int r) {return a[r] - (l ? a[l-1]*p[r-l+1] : 0);}
};
// 示例:计算"bcd"的哈希(假设base=233)
hash(1,3) = a[3] - a[0]*p[3] = (a*B^3 + b*B^2 + c*B^1 + d*B^0) - a*B^3= b*B^2 + c*B^1 + d*B^0 = hash(1,3)
操作 时间复杂度 空间复杂度
预处理 O(n) O(n)
子串查询 O(1) -

这就意味着,给定一个或多个字符串,我们可以瞬间判断出一个字符串的某子串和另一字符串的某子串是否相等,无需任何比较!

「赖皮」之道:四两拨千斤的解题哲学

在算法江湖中,字符串哈希被戏称为「赖皮算法」——它不似正统数据结构那般严谨,却总能用巧劲化解难题。其精髓在于:将字符串问题暴力转化为数字问题

1 回文判定(Palindrome Detection)

正统解法:Manacher算法(O(n))

哈希赖皮法

  1. 准备原字符串和原字符串的反转的前缀进制哈希。正向计算前缀哈希,反向计算后缀哈希
  2. 比较子串[l,r]的正向哈希与反向哈希

2 字符串匹配(Pattern Matching)

正统解法:KMP(O(n+m))

哈希赖皮法

  1. 预处理模式串哈希H(pattern)
  2. 滑动窗口直接计算文本串所有长度为m的子串哈希

3 最长重复子串(Longest Repeated Substring)

正统解法:后缀数组(O(nlogn))

哈希赖皮法

  1. 二分可能的最大长度L
  2. 用哈希存储所有长度为L的子串,检查碰撞

4 最长公共子串(Longest Common Substring)

正统解法:后缀自动机(O(n))

哈希赖皮法

  1. 二分可能的最大长度L
  2. 分别计算两个字符串所有长度为L的子串哈希集
  3. 求哈希集合的交集

5 循环同构判定(Cyclic Isomorphism)

正统解法:最小表示法(O(n))

哈希赖皮法

  1. 构造原字符串的哈希环S = S + S
  2. 比较所有可能旋转位置的子串哈希

江湖箴言
"哈希算法七分险,快剑无影胜有影。
若遇生死决赛场,双哈希出保太平。"

处理哈希碰撞问题

选择模数

飞刀轻快但易折,重剑无锋却难精,双剑合璧则近乎无敌。不同的场景需用不同的方法。

  1. 小质数模数(如 1e9+7)需显式取模,常数较大,且碰撞风险高:若数据量超过 \(\sqrt{p}\)(约3e4),生日悖论导致碰撞概率显著上升

    • 例如:用p=1e9+7处理1e5个字符串时,碰撞概率约5%
    • 适用场景
      • 小规模数据(n≤1e4)
      • 需严格控制哈希值范围的场景
  2. 大质数模数(如 1e18+3)的碰撞概率极低:值域巨大,可安全处理 1e6 级数据。不过计算的代价增加。自己选取大质数,更拥有抗构造性,难以针对随机大质数构造碰撞数据。

  3. 自然溢出(\(2^{64}\))飞刀迅捷,却怕预判。利用 CPU 和无符号整数自动溢出,无显式取模操作,非常高效。但模数固定为\(2^{64}\),数字虽大,但已经确定。攻击者可构造全冲突数据。如 Thue-Morse 序列可导致大规模碰撞

    • 适用场景
      • 非对抗性环境(如企业内部数据处理)
      • 时间极其敏感的竞赛场景
  4. 双哈希(Dual Hash)子母鸳鸯,万无一失。同时使用两个不同基数和模数的哈希系统,例如:(base1=131, mod1=1e9+7) + (base2=13331, mod2=1e18+3)

    • 优势
      • 需两个哈希值同时碰撞才算冲突,概率非常非常非常低
      • 即使攻击者破解一组参数,另一组仍可保障安全
    • 代价
      • 空间和时间翻倍

对于 \(n\) 个字符串和模数 \(p\),生日悖论给出的冲突概率:

\[P \approx 1 - e^{-\frac{n(n-1)}{2p}} \]

场景 推荐策略 理由
竞赛常规题 自然溢出 代码简洁,跑得快
对抗性构造数据 双哈希 绝对安全
超大字符集(如Unicode) 大质数+双哈希 避免基数不足导致冲突
内存敏感环境 单大质数 平衡安全与空间

选择基数

基数选择也对碰撞概率有影响。首先基数应该至少和字符集一样大,其次优质基数应满足三大特征:

  1. 与模数互质:若模数为质数,则基数只需非其倍数。互质可以避免出现周期性重复,最大化利用值域空间(如base=2时哈希值奇偶性固定)

  2. 远离模数的二次剩余:防止出现 \(base^k \equiv 1 \ (\text{mod} \ p)\) 的短周期。例如base=10p=1e9+7的组合周期仅为\(p-1\),实际效果差

  3. 高熵分布:推荐使用不规则大质数

优质基数可将冲突率再降一个数量级

基数类型 实际冲突率(n=1e5, p=1e9+7)
小质数(131) ~0.5%
规律数(10007) ~1.2%
大随机质数 <0.01%
class hashstr {// ...bool use_mod;const static int default_base = 131;int base;
public:// 构造函数:可指定模数(0表示自然溢出)、基数hashstr(const string &s, u64 mod = 0, int base = default_base) : n(s.size()), use_mod(mod != 0), mod(mod), a(n), p(n+1), base(base) {u64 x = 0;for (int i = 0; i < n; ++i) {int d = s[i];  // 直接使用ASCII码,支持更广的字符集if (use_mod) {a[i] = (x * base + d) % mod;} else {a[i] = x * base + d;}x = a[i];}p[0] = 1;for (int i = 1; i <= n; ++i) {if (use_mod) {p[i] = (p[i-1] * base) % mod;} else {p[i] = p[i-1] * base;}}}// 获取子串哈希 [l, r]u64 hash(int l, int r) {if (use_mod) {u64 result = (a[r] - (l ? (a[l-1] * p[r-l+1]) % mod : 0) + mod) % mod;return result;} else {return a[r] - (l ? a[l-1] * p[r-l+1] : 0);}}
};

(醒木一拍)

《哈希江湖志》

进制为基化剑芒,
子串快剑破风霜。
小质易折如薄柳,
大模稳坐似山冈。

自然溢出飞刀迅,
双哈希出鬼神慌。
莫道此招多取巧,
九成胜算即称王!

(醒木再拍)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/905736.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HW-1

1.选项A是正确的,它表示的是极小项m6的正确形式。极小项m6对应的是变量a=0,b=1,c=1,d=0的情况,因此其表达式应为(\overline{a} \cdot b \cdot c \cdot \overline{d}),即选项A。 其他选项的分析:选项B是一个或项,不符合极小项的定义。 选项C缺少变量a和d,不是一个完整的…

为什么nn.Linear 的weight 是 (out_features, in_features)

在PyTorch的nn.Linear中,权重矩阵的形状为(out_features, in_features)。这是因为线性变换的实现方式为:具体来说:当创建nn.Linear(10, 60)时,in_features=10,out_features=60,因此权重的形状是(60, 10)。 输入张量t的形状为(2, 5, 10),与转置后的权重a.weight.T(形状(…

字符串问题的终极法宝:进制哈希

江湖中,剑客以快制胜,而算法竞赛里,字符串哈希(String Hashing)便是那柄出招如电的快剑。 各种字符串问题纷乱复杂,各种字符串算法招式繁复,需苦练内功心法。但字符串哈希算法却只凭一招:将字符串化作数字,以数论为刃,至简之道斩尽来犯之敌。 但此招并非无懈可击。若…

布局机器人,vivo用三十年技术沉淀回答“时代之问”

科技创新给产业界带来最大的影响就是“跨界焦虑”。不敢错过,这是企业面对风口的普遍心态。生怕一波没跟上,就被时代淘汰了。 但不错过,并不等于抓得住。比如元宇宙概念经历资本狂欢,无数科技巨头卷入,退潮时一地鸡毛。再比如在新能源汽车赛道卷起千亿投资风暴时,也有很多…

石油化工厂区防爆入侵报警系统

石油化工厂区防爆入侵报警系统采用AI智能防爆预警摄像头嵌入AI人体识别深度算法,对人体目标特征的检测分析识别预警。系统设备采用AI算法,通过大量真实的场景样本训练后,能够在各种应用场景下及时准确地对场景中发生的人体入侵行为发出告警信息。通过对实时视频图像进行智能…

矿山皮带运输机安全监测预警系统

智慧矿山皮带运输机安全监测预警系统是基于AI视频监测分析识别的智能皮带管理系统,该系统通过将人工智能识别算法提高实时分析的效率,达到现场快速识别、分析、预警的目的,为矿山皮带运输系统提供可视化的安全生产保障,该系统已经实现了皮带运输机大媒块、传输带异物、传输…

OP253自动安装凸轮弹簧常见问题

1.夹爪抓取弹簧后不停扔料 两台弹簧振动盘里面的弹簧是镜像件,如果出现混料被取走,会导致卷簧时卡死。夹爪夹住弹簧并提起时,有两个接近开关会检测弹簧是否正确,如果不正确,会将弹簧扔进废料桶。上图两个接近开关是埋入式,只有中心位置检测。应该左边不亮,右边亮,如果任…

web作业

制作一个学生管理系统包括按钮和输入框学生成绩管理系统body { font-family: Arial, sans-serif; margin: 20px } \3c pre>\3c code> h1 { text-align: center } .add-student, .search, .filters { margin-bottom: 20px } table { width: 100%; border-collapse: col…

linux主机新增硬盘与挂载

近期对工作站主机新增了硬盘,网上搜罗了一些教程,整合了一下,感谢相关博主的知识分享,本篇只是整合参考:https://zhuanlan.zhihu.com/p/117651379 https://cn.linux-console.net/?p=10440新增硬盘处理 新增硬盘后,在linux系统下输入 fdisk -l 命令查看当前磁盘信息可以看…

如何优化SQL查询以提高数据库性能?

你正在自助餐厅,所有的食物看起来都很美味。但你不是拿一个盘子,只取你需要的,而是开始从各个角落堆满食物,弄得一团糟,速度也慢了下来。结果是什么?你拿的东西很多并且效率低下。 这就像没有优化的SQL查询!它们加载了不必要的数据,拖慢了整个系统的速度,并在数据库中…

HTB Season7 Dog

一、信息收集 拿到ip,先测试一下连通性连通性正常,使用nmap扫描一下ip,输入nmap -sV -sC -A 10.10.11.58 -Pn 扫描发现http页面,并且发现了网页使用了BackDropCMS在hosts内添加添加页面,访问网页发现一个登录框,尝试一下sql注入无果,只能另寻出路此时dirsearch扫描发现了…