算法学习笔记(14): 字符串哈希

算法,学习,笔记,字符串,哈希 · 浏览次数 : 50

小编点评

**字符串Hash哈希的玄学方法** 字符串Hash哈希是一种玄学的方法,其目的是将信息通过某种方式的缩减,映射到某一个值域上,从而表示这个信息。 **主要方法:** * **链表质数后移:**在哈希表中有两个处理方法:链表质数后移(向后移动质数位,知道找到一个空的地方为止)和链表插入。 * **双模数和双底数:**这种双保险方法是指双模数和双底数,即值域和 `base` 不一样。 **优点:** * 通过预处理,可以快速计算任何字符串的哈希值。 * 能够扩展到二维甚至更高维空间。 * 避免超慢的取模运算。 **缺点:** * 算法可能很复杂,对性能可能有所影响。 * 存在哈希冲突的可能。 **应用场景:** * **字符串匹配** * **字符串处理** * **哈希表** **建议:** * 在使用字符串Hash哈希之前,应该考虑其性能和安全性。 * 使用不同的`base`值可以降低哈希冲突的概率。 * 使用双哈希可以提高安全性和性能。

正文

字符串Hash

哈希是一个玄学的方法……最适骗分法

哈希,指将信息通过某种方式的缩减,映射到某一个值域上,从而表示这个信息。

如果有两个信息同时映射到了同一个位置,那么就会产生哈希冲突

哈希冲突在哈希表中有两种处理方式:

  • 链表

  • 质数后移(向后移动质数位,知道找到一个空的地方为止)

但是,在OI中,哈希冲突恰恰是毒瘤出题人卡你正确性的方法……关键是我们不一定能够保证没有哈希冲突……QwQ

所以,哈希很玄学,慎用!

字符串哈希

一般来说,字符串哈希在OI中常用的公式是 \(hash(str) = \sum_{i=1}^n base^{n-i} \times str_i\)

在生产环境中,可以有 time33, md5, sha256 等经典算法。但是这里用不到……

那么用这个公式有什么好处呢?

  • 可以通过 \(O(n)\) 的预处理,在 \(O(1)\) 得出任意一个子串的哈希值

  • 可以随意拼接两个字符串,在 \(O(1)\) 内得出合并后的哈希,并在 \(O(m)\) 时间内再处理

  • 可以扩展到二维甚至更高

  • ……

不扯了……讲实现了

由于我们需要映射到某一个值域上,所以,一般来说,我们取为 unsigned long long 可以表示的值域即可。(这样也有一些其他的好处,自然溢出避免了超慢的取模运算)

那么,我们以字符串 jeefybase13 为例:

Text Hash Calc
j 106 \(0 \times 13\) + 'j'
je 1479 \(106 \times 13 +\) ’e'
jee 19328 \(1479 \times 13\) + 'e'
jeef 251366 \(19328 \times 13\) + 'f'
jeefy 3267897 \(251366 \times 13\) + 'y'

生成代码如下

我们考虑换一种表示法:

Text Calc
j \(13^0 + ord(j)\)
je \(13^1 \times ord(j) + 13^0 \times ord(e)\)
jee \(13^2 \times ord(j) + 13^1 \times ord(e) + 13^0 \times ord(e)\)
jeef \(13^3 \times ord(j) + 13^2 \times ord(e) + 13^1 \times ord(e) + 13^0 \times ord(f)\)
jeefy \(13^4 \times ord(j) + 13^3 \times ord(e) + 13^2 \times ord(e) + 13^1 \times ord(f) + 13^0 \times ord(y)\)

想必,有了这个表之后,哈希拼接应该就很简单了吧。

\[Hash(T) = Hash(A) \times 13^{|B|} + Hash(B) \]

\(|B|\) 指的是字符串的长度

我们可以预处理出 \(base^i\)

hashInt bofs[N] = {1}; // base_offset
for (int i = 1; i < N; ++i) bofs[i] = bofs[i - 1] * base;

那么两个计算如下

struct String {
    string s;
    hashInt ha;
}

String merge(String A, String B) {
    String s = {A.s + B.s, A.ha * bofs[B.s.size()] + B.ha};
    return s;
}

没有实际运行过程序,请自行斟酌

同理,如果我们需要字符串片段的哈希,我们可以通过合并的逆操作……

hashInt ha[N];
hashInt slice(int l, int r) { // [l, r]
    return ha[r] - ha[l - 1] * bofs[r - l + 1];
}

那么,恭喜你,掌握了哈希的大部分用法了。

房卡小专题

总所周知,哈希是出题人特别喜欢卡的一种做法,所以,我们需要较少减少哈希冲突的可能。那么比较好的解决方法是双哈希。而双哈希指的是双模数和双底数,也就是说值域和 \(base\) 都不一样。这种双保险的方法是很难被卡的。

练习题

[CTSC2014] 企鹅 QQ - 洛谷

【模板】树同构([BJOI2015]树的同构) - 洛谷

没想到吧,同构也可以通过哈希来!

[JSOI2008]Blue Mary的战役地图 - 洛谷

这道题可以用二维哈希

小知识

其实C++也是有内置的哈希函数的,参考std::hash - cppreference.com

与算法学习笔记(14): 字符串哈希相似的内容:

算法学习笔记(14): 字符串哈希

# 字符串Hash > 哈希是一个玄学的方法……最适骗分法 哈希,指将信息通过某种方式的缩减,映射到某一个值域上,从而表示这个信息。 如果有两个信息同时映射到了同一个位置,那么就会产生**哈希冲突**。 **哈希冲突**在哈希表中有两种处理方式: - 链表 - 质数后移(向后移动质数位,知道找到一个

算法学习笔记(25): 矩阵树定理

# 矩阵树定理 > 本文不作为教学向文章。 > > 比较好的文章参考: > > - [矩阵树-定理以及凯莱公式](https://zhuanlan.zhihu.com/p/593934554) > > - [【学习笔记】矩阵树定理(Matrix-Tree)_繁凡さん的博客-CSDN博客](https

算法学习笔记(6): 树链剖分

树链剖分 树链剖分是一个很神奇,但是在树上可以完成一些区间操作问题 简单来说,就是把一棵树分成一条条的链,通过维护链上的信息来维护整棵树的信息 基础知识可以参考我的另外一篇博客:算法学习笔记(5): 最近公共祖先(LCA) 这里假设你已经掌握了上述博客中的所有相关知识,并清晰了其背后的原理 性质?发

算法学习笔记(11): 原根

原根 此文相对困难,请读者酌情食用 在定义原根之前,我们先定义其他的一点东西 阶 通俗一点来说,对于 $a$ 在模 $p$ 意义下的阶就是 $a^x \equiv 1 \pmod p$ 的最小正整数解 $x$ 或者说,$a$ 在模 $p$ 意义下生成子群的阶(群的大小) 再或者说,是 $a$ 在模

算法学习笔记(30):Kruskal 重构树

Kruskal 重构树 这是一种用于处理与最大/最小边权相关的一个数据结构。 其与 kruskal 做最小生成树的过程是类似的,我们考虑其过程: 按边权排序,利用并查集维护连通性,进行合并。 如果我们在合并时,新建一个节点,其权值为当前处理的边的权值,并将合并的两个节点都连向新建的节点,那么就可以得

算法学习笔记(3.1): ST算法

ST表 在RMQ(区间最值)问题中,著名的ST算法就是倍增的产物。ST算法可以在 \(O(n \log n)\) 的时间复杂度能预处理后,以 \(O(1)\) 的复杂度在线回答区间 [l, r] 内的最值。 当然,ST表不支持动态修改,如果需要动态修改,线段树是一种良好的解决方案,是 \(O(n)\

C++算法之旅、09 力扣篇 | 常见面试笔试题(上)算法小白专用

算法学习笔记,记录容易忘记的知识点和难题。详解时空复杂度、50道常见面试笔试题,包括数组、单链表、栈、队列、字符串、哈希表、二叉树、递归、迭代、分治类型题目,均带思路与C++题解

C++算法之旅、08 基础篇 | 质数、约数

算法学习笔记,记录容易忘记的知识点和难题。试除法、分解质因数、筛质数、约数个数、约数之和、最大公约数

算法学习笔记(1): 欧几里得算法及其扩展

扩展欧几里得算法详解 在了解扩欧之前我们应该先了解欧几里得算法 欧几里得算法 这是一个递归求最大公约数(greatest common divisor)的方法 $$ gcd(a, b) = gcd(b, a % b) $$ 可以通过一个类似的简单公式推导而来 好像叫做辗转相减法来着? $$ gcd(

算法学习笔记(3): 倍增与ST算法

倍增 目录倍增查找 洛谷P2249重点变式练习快速幂ST表扩展 - 运算扩展 - 区间变式答案倍增更多的用法优化矩形查询优化建图优化 DP作者有话说 倍增,字面意思即”成倍增长“ 他与二分十分类似,都是基于”2“的划分思想 那么具体是怎么样,我们以一个例子来看 查找 洛谷P2249 依据题面,我们知