2.哈希表

具体可参考y总的视频~欢迎学习交流~

文章目录

  • 字符串哈希
  • 存储方式
    • 开放寻址法
    • 拉链法

一、字符串哈希

基础理解

1.字符串哈希:把不同的字符串映射成不同的p进制数字

2.对于一个长度为n的字符串s,如何映射成p进制数字呢?这就要引入Hash函数h(s)=\sum_{i=1}^{n}s[i]\times p^{^{n-i}}(mod M )  和asc表

比如字符串abc,它对应的p进制数字(哈希函数值)为a\times p^{2}+b\times p^{1}+c\times p^{0}=97\times 131^{2}+98\times 131^{1}+99

3.哈希碰撞:两个字符串不同,但p进制数字(哈希函数值)相同

4.如何解决哈希碰撞呢?巧妙设置p和M的值,保证p和M互质,p通常取131或13331,M通常取2^{64}

进阶理解

1.求字符串的前i项的哈希值相当于求前缀和     前缀和h[i]=h[i-1]\times p+s[i],h[0]=0

    理由如下图:

2. 求字符串的字串的哈希值相当于求区间和    区间和h[l,r]=h[r]-h[l-1]\times p^{r-l+1}

     理由如下图:

   

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;
}

 
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值