开放寻址法哈希&字符串哈希

开放寻址法:

开放寻址法的数组长度最好是题目哈希的数据范围的2-3倍,其次模的那个数应该是符合前面这个条件的第一个质数且离2的幂次尽量得远(这样冲突会比较少)

找质数:

比如题目哈希的数据范围是1e5,那我们的N首先为2e5,然后再找大于2e5的第一个质数,N即为该数,下面为找质数的代码

for(int i = 2e5; ; i++)
    {
        bool flag = true;
        for(int j = 2; j * j <= i; j++)
            if(i % j == 0)
            {
                flag = false;
                break;
            }
        if(flag)
        {
            cout << i << endl; //输出的i即为大于2e5的第一个质数
            break;
        }
    }

开放寻址法的核心是find函数:如果已经存在x的话,就返回x的映射k的值,若不存在,就返回x应该放的值,也就是k;

int find(int x)
{
    int k = (x % N + N) % N; 
    //如果x是负数的话,直接%N是一个负数,但放的地址都是正的,所以先%N再+N,最后再%N;

    while(h[k] != null && h[k != x) //这个坑位上有人,并且不是自己,就要找下一个坑位;
    {
        k++;
        if( k == N) k = 0;
        //如果k已经找到最后一个位置了的话,就让k从第一个位置开始找,是一个循环的过程;
    }
    
    return k;
    //如果存在x的话,就返回x的映射k的值,若不存在,就返回x应该放的值,也就是k;
}

.......................................................................................................

字符串哈希(前缀和字符串):

作用:

一般是判断两个区间的子字符串是否相等

求字符串哈希的时候首先预处理出来所有前缀的哈希:

比如:h[1]表示前1个字符的哈希值,h[2]表示前2个字符的哈希值,以此类推,h[0] 特殊处理=0

那么如何来定义某个前缀的哈希值:

把字符串看作是一个P进制的数,每一位上的字母表示P进制上的每一位数字

把字符串的哈希值算出来后(因为该值很大,所以还要模上一个Q)

注意:

1.一般情况下不要把某个字母映射成0

2.哈希数字的时候是有冲突的且要解决冲突,即上面的开放寻址法;但哈希字符串的时候是假设人品足够好,不会出现冲突

3.P取131或 13331,Q取2^64,在这么取的情况下,基本可以假定不会出现冲突

4.因为unsigned long long 是2^64,所以用ULL来存h[],这样就不需要取模了(溢出相当于取模)

 

我们可以利用前面求出来的前缀哈希,可以求出任一字串的哈希:

比如想求出[L,R]的哈希值,因为预处理,已知[1,R]和[1,L-1]的哈希值,即h[R]和h[L-1]

[1,R]: 最高位是P^(R-1) (1对应的位置),最低位是P^0 (R对应的位置)

[1,L-1]:最高位是P^(L-2) (1对应的位置),最低位是P^0 (L-1对应的位置)

1.所以要先把[1,L-1]这段往左移,(为什么要向左移,因为h[R]和h[L-1]的前L-1个字母是相同的,但因为位数不同,乘的权值也不同即乘P的次方不一样,所以要使前L-1个字母往左移,这样他们两个前L-1个字母都对应的同样的权值,最后再相减)使最高位对齐,即h[L-1] * (R-L+1)

2.[L,R]的哈希值即为 h[R]-h[L-1] * (R-L+1)

3.后面乘的这个(R-L+1)用一个p[]预处理出来

预处理代码:

ULL h[N], p[N];
    

    p[0]=1; 
    for(int i=1;i<=n;i++)
    {
        p[i]=p[i-1]*P; //p[i]=P的i次方;
        h[i]=h[i-1]*P+str[i]; //这里的str[i] 是对应的ASCII码值;
    }

判断代码:

ULL get(int l, int r)
{
    return h[r] - h[l-1] * p[r-l+1]; //这里就是前面预处理p[]的作用,p[i]表示P^i
}


while(m--)
    {
        int l1, r1, l2, r2;
        scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
        if(get(l1, r1) == get(l2, r2)) puts("Yes");
        else puts("No");
    }

...........................................................................................................................................................

另外还要说一种字符串哈希(区别于上面的前缀和哈希):

作用:

这种字符串哈希作用一般是预处理打表,差不多就是不需要预处理前缀和,而是直接求得每个字符串的哈希值,然后打表

下面给个代码:

ULL Hash(char *str)
{
    ULL res = 0;
    while(*str) res = res*P+*str, ++str; //这步
    return res;
}

int find(char *str)
{
    int p = Hash(str) % MOD;
    
    //这里就和开放寻址法类似:ht[p][0]不为0且p位置存的不是他本身(用的是strcmp函数)p就++,直到        
    找到位置
    while(ht[p][0] && strcmp(str, ht[p])) p++;
    return p;
}

最后欢迎大家留言交流或指正!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值