具体可参考y总的视频~欢迎学习交流~
文章目录
- 字符串哈希
- 存储方式
- 开放寻址法
- 拉链法
一、字符串哈希
基础理解
1.字符串哈希:把不同的字符串映射成不同的p进制数字
2.对于一个长度为n的字符串s,如何映射成p进制数字呢?这就要引入Hash函数 和asc表
比如字符串abc,它对应的p进制数字(哈希函数值)为
3.哈希碰撞:两个字符串不同,但p进制数字(哈希函数值)相同
4.如何解决哈希碰撞呢?巧妙设置p和M的值,保证p和M互质,p通常取131或13331,M通常取
进阶理解
1.求字符串的前i项的哈希值相当于求前缀和 前缀和
理由如下图:
2. 求字符串的字串的哈希值相当于求区间和 区间和
理由如下图:
3.代码如下:
typedef unsighed long long ULL;//定义无符字长整型
const int P=131;
ULL p[N],h[N];//p[i]为p^i h[i]为前i项的哈希值
//预处理前缀和
void init()
{
p[0]=1,h[0]=0;
for(int i=1;i<=n;i++)
{
p[i]=p[i-1]*P;
h[i]=h[i-1]*P+s[i];
}
}
//计算s[l~r]的哈希值
ULL get(int l,int r)
{
return h[r]-h[l-1]*p[r-l+1];
}
计算前缀和的时间复杂度为O(n)
计算区间和的时间复杂度为O(1)
二、存储方式
以下两种方法类似于找坑位
与离散化思想类似,当我们要对大范围的复杂信息进行统计时,通过映射把这些信息映射到小范围 的容易维护的值域内,但由于范围变小,会造成两个不同的信息被哈希函数映射为相同的值,这种情况为哈希冲突。
开放寻址法
如果发生哈希冲突时,相当于要找的坑位有人了,我们可以去找下一个坑,直到找到坑为止,这就叫开放寻址法。
代码如下:
const int N = 2e5 + 3; //开放寻址法一般开数据范围的 2~3倍,而且取质数冲突概率最小,这样大概率就没有冲突了
const int null = 0x3f3f3f3f;
int h[N];//h[i]为第i个坑位 (一维数组h相当于一排坑)
//找x在哈希表中的位置(坑位)
int find(int x) {
int t = (x % N + N) % N;//t为x的坑位
while (h[t] != null && h[t] != x) {
t++;
if (t == N) t=0;//如果找到最后一个坑还没找到坑位的话,就再去第一个坑从前往后找
}
return t;
}
拉链法
如果发生哈希冲突时,相当于要找的坑有人了,我们可以这个坑位排队,根据排队的特征就想到了链表,这就叫拉链法
代码如下:
const int N = 1e5 + 3; // 取质数冲突的概率最小,这样大概率就不会有冲突了
int h[N], e[N], ne[N], idx; //h[i]为第i个坑位 e[idx]为idx这个节点的值 ne[idx]为idx这个节点->所指向的结点 idx为准备要用的结点
//把x放进位置k的槽中(在坑位k排队)
void insert(int x) {
int k = (x % N + N) % N;
e[idx] = x;
ne[idx] = h[k];
h[k] = idx++;
}
//位置k的槽中找x(在坑位k的队伍中找x)
bool find(int x) {
int k = (x % N + N) % N;
for (int i = h[k]; i != -1; i = ne[i]) {//从位置k向下找
if (e[i] == x) {
return true;
}
}
return false;
}