每日一句
It’s no use going back to yesterday, because I was a different person then.
回顾昨天毫无用处,因为我已今非昔比
这是个好东西,值得学习一下。
Hash 的思想
这一节更多地涉及到数学方法,不喜的可以跳过。
哈希之所以广泛存在,是因为它能在绝大多数情况下可以在O(1)的时间复杂度中完成元素的查找。
Hash 的核心思想在于,将输入映射到一个值域较小、可以方便比较的范围。
注意:这个范围在不同情况下意义不同,注意理解。
介绍下面的几种情况
-
在哈希表中,值域需要小到能够接收线性的空间与时间复杂度
-
在字符串哈希中,值域需要小到能够快速比较( 1 0 9 、 1 0 1 8 10^9 、10^18 109、1018 都是可以快速比较的),同时为了降低哈希冲突率,值域也不能太小。
哈希碰撞
我们定义一个把字符串映射到整数的函数 $f
$,这个 f 称为是 Hash 函数。
我们希望这个函数 f 可以方便地帮我们判断两个字符串是否相等。
具体来说,哈希函数最重要的性质可以概括为下面两条:
-
在 hash 函数值不一样的时候,两个字符串一定不一样
-
在 Hash 函数值一样的时候,两个字符串不一定一样(但有概率一样,且我们当然希望它们总是一样的)
Hash 函数值一样时原字符串却不一样的现象我们称之为哈希碰撞。
Hash 函数定义
一般情况下,我们需要关注的是:时间复杂度和 Hash 的准确率。
常见的有下面的两种Hash函数的定义:
-
通常我们采用的是多项式 Hash 的方法,对于一个长度为 l 的字符串 s 来说,我们可以这样定义多项式 Hash 函数: f ( s ) = ∑ i = 1 l s [ i ] × b l − i ( m o d M ) f(s)=\sum_{i=1}^{l} s[i] \times b^{l-i}(\bmod M) f(s)=∑i=1ls[i]×bl−i(modM)。例如,对于字符串 xyz,其哈希函数值为 $xb^2 + yb + z
$。 -
也有很多人使用的是另一种 Hash 函数的定义,即 f ( s ) = ∑ i = 1 l s [ i ] × b i − 1 ( m o d M ) f(s)=\sum_{i=1}^{l} s[i] \times b^{i-1}(\bmod M) f(s)=∑i=1ls[i]×bi−1(modM),这种定义下,同样的字符串 $xyz
$的哈希值就变为了 $x + yb + zb^2 $了。
显然,上面这两种哈希函数的定义都是可行的,但二者之后会讲到的计算子串哈希值所用的计算式是不同的,因此千万注意 不要弄混了这两种不同的 Hash 方式。
哈希碰撞概率
上面提到了两个Hash函数的定义,这里我们讲讲如何选择 M和计算哈希碰撞的概率。
这里M需要选择一个以(至少要比最大的字符要大),b可以任意选择。
我们用未知数 x 替代b,那么 f ( s ) f(s) <