哈希学习笔记
2023-01-20 22:33:34
## 前言
### 理解
将一个内容映射成另一个内容,如:
1. 班级学生学号
2. 打车/外卖 手机尾号
3. 口红色号
### 简介
将一个内容映射进一个数组(哈希表)
例如 H(X) = X % 11
可以将任意一个数组映射成区间 [1,11) 之间的数
### 冲突
可能会有数 $\%$ 一个数在同一个位置,我们有两个办法解决
1. 加上一个数,直到一个空的位置(但是查询很慢)
2. 使用 `vector`,制作一个类链表的结构(会被hack,依然很慢)
3. 尽可能分散(设计一个好的哈希函数)
### 常识
Hash(x) = x % p,p 一般为素数(可以用9999971)
- p 为容量
- 元素总数 / 容量 = 负载因子(α,阿尔法)
##
---
## 设计函数
### 定义
定义一个集合 s,s = $\overline {s_1 s_2 \ldots s_n},s_i \in \{'a' \ldots 'z'\}$
定义一个集合 c,$c_i = s_i - 'a'$
Hash(s) = $ (\displaystyle \sum_{i=1}^n {c_i * base^ {n-1}})$ mod p
base 是一个指定的数,一般是一个大于字符集中字符数量(c 的最大值)的素数,比如此处可以取 base = 137
```cpp
for(int i = 0; i < n; i++)
res = (res * base + (s[i]-'a')) % p;
```
双哈希(减少重复概率,更加方便):选两个 base
### 区间
在处理过程中,用数组 a 记录下计算 Hash(s) 的过程
也就是说,a[i] = Hash($\overline {s_1 s_2 \ldots s_i}$)
定义 $s_{l,r}$ = $\overline {s_l s_{l+1} \ldots s_r}$
当我们求任意子串 $s_{l,r}$ 的哈希值时,可以快速求出
Hash($s_{l,r}$) = ($a[r] - a[l-1] * base^{r-l+1}$) mod p
```cpp
for(int i = 1; i <= n; i++){
res = (res * base + (s[i]-'a')) % p;
a[i] = res;
}
```
```cpp
int t = a[l-1] * pow(base,r-l+1) % p;
return (a[r] - l + p) % p;
```
关于 `(a[r] - l + p) % p`,是为了解决可能存在负数的问题
```cpp
unordered_map<string,int > hash_table;
```
一种类似于 map 的数据结构,可以解决大多数哈希表的操作
unordered_map 底层是用哈希表实现的,map 底层是用红黑树实现的
```cpp
if(hash_table.find("qwq") == hash_table.end()) ;//找不到
else ;//找得到
```