完成字符串哈希的描述

完善字符串哈希部分的操作步骤描述
This commit is contained in:
XiaoqiangWu 2022-12-15 11:22:01 +08:00 committed by wuxiaoqiang12
parent b3d642fa85
commit be0abac555

View File

@ -300,3 +300,54 @@ $$
- 尽量少地发生哈希冲突;
- 时间复杂度 $O(1)$ ,计算尽可能高效;
- 空间使用率高,即 “键值对占用空间 / 哈希表总占用空间” 尽可能大;
## 常用哈希函数
下面介绍两种常见的哈希类型,数字哈希和字符串哈希。
### 数字哈希
对于需要将数字哈希化的场景,常采用对大质数取模的方式作为哈希函数以此来减少冲突。若用 $N$ 来表示所选取的大质数,则哈希函数 $f(x)$ 可表达为
$$
f(x) = (x (\mod N) + N)(\mod N)
$$
该函数在第一次取模后还有一次加 $N$ 的操作是因为在某些编程语言如 C/C++ 中负数取模会得到负数,这与数学中的取模运算定义不同(数学定义中无论正负数取模的结果均为正数)。另外,我们常常将取模后的值作为数组中的索引,这样正数也能保证数组索引的正确性。
大质数的选择取决于你希望哈希后的长度,若一堆数字你希望哈希后分布在 $0-n$ 之间,则 $N$ 选择大于 $n$ 的大质数即可。选择算法示例如下:
=== "C"
```c title="prime_num_sel.c"
int main() {
for (int i = 1000; ;i++) {
int flag = 0;
for (int j = 2; j < i; j++) {
if (i % j == 0) {
flag = 1;
break;
}
}
if (flag == 0) {
printf("the prime number is %d", i);
break;
}
}
return 0;
}
```
### 字符串哈希
这里介绍一种被称作“字符串前缀哈希”的算法来实现字符串的哈希化。若有一字符串 `str = "abcedacbd"`,则哈希表中的元素表示从左边起到第 `i` 个位置上的子字符串的哈希值。即,`f(1) -> "a"; f(2) -> "ab"; f(3) -> "abc"`,以此类推。
具体过程如下
1. 将字符串存储在下标为 `1``char` 数组中;
2. 计算字符串的前缀数字,将他们哈希化,将字符串看作是一个 `P` 进制数,这里 `P = 131` 或者 `P = 13331`。按照经验,这两个值对字符串哈希会产生非常小的冲突可能。
3. 将这个 `P` 进制数转换为10进制通常转换出来的数字会很大则将其对 `Q` 取模。这样任意一个字符串都可以转换成 $0 \cdots Q - 1$ 之间的数字:$(1234)_P => (1 * P^3 + 2 * P^2 + 3 * P^1 + 4 * P^0) \mod Q$
4. 对于`Hash`函数,将计算字符串中从 `l``r` 的哈希值,通过前缀哈希表以及下公式计算,注意左侧为低位,右侧为高位。
`f(r) - f(l - 1) * p[r - l + 1]` ,其中 `p` 数组存放了从 $P^0 \sim P^N$ 的所有数字,方便查询。