Hash

update:2021.7.15 补充了更广泛,更丰富的乱搞Hash方法

求hash的方法:

1.除留余数法:

h(k)=k mod m,m一般为质数

2.平方取中法:

h(2333)=2333*2333=5442889

3.折叠法:

h(123456789)=123+456+789

4.乘积取整法

...

 

哈希冲突?

哈希这个算法本来就离谱有冲突不是很正常的事吗

1.线性探测法(线性散列法)

添加元素时,使用散列函数确定元素的插入位置,如果此空间有值:
1.该值是所要插入元素的关键码,不进行插入。
2.产生冲突,依次查看其后的下一个桶,如果发现空位置插入新元素

 

 

 2.二次探测法(二次散列法)

一次散列法有个缺点就是元素碰撞容易引起连锁反应,形成较长连续被占单元。

于是我们对解决冲突的方法进行优化:

H产生冲突,依次查看H+1^2,H+2^2,H+3^2...,如果发现空位置插入新元素

但是,这样写仍然有不足:

乘法和取模运算会使计算复杂性增加而变得不实用。

于是我们可以将乘法移位转化为加法移位:

从H+i^2移到H+(i+1)^2,移动了2*i+1个位置。

乘法变加法,岂不美哉?

等等,你问为什么二次探测法会比线性探测法优?

那么我会告诉你:

OI不需要证明

定理:如果采用二次探测法,且单元长度为质数,如果表至少有一般空单元,新的元素总能被插入。而且插入过程中,没有一个单元被探测两次。

证明:

设有H个单元被探测到了两次,则有:

H+i^2≡H+j^2(mod M)

i^2-j^2≡0(mod M)

(i+j)(i-j)=0,与我们的假设矛盾。

所以没有一个单元被探测两次。

3.拉链法(再散列法)

你问我还有没有更NB的方法?

当然有!

 

 

 对于Hash值相同的数,我们不假装看不到,也不乱七八糟地移来移去,我们用vector数组将Hash值相同的元素以链表的形式存下来。

相较于前面奇奇怪怪的方法,这种方法看起来是最正常的了。。。

 

字符串Hash

 

https://www.luogu.com.cn/blog/dlp/zi-fu-chuan-hash

以前写的Hash都是假的...

用一道板题来进行Hash的学习:

题意:给定一个·只包含A,G,C,T的字符串,求长度为k的相同连续子串的最大总数。

题目链接

Code:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define int long long
 4 const int N=5e6+5;
 5 const int BASE=131;//Hash的思想就是将一个字符串转化为一个BASE进制的数
 6 const int MOD=4999957;//转化后的数是很大的,所以需要模一个数。MOD的大小一般>1e6
 7 string s;
 8 int k;
 9 int maxn=0;
10 int now;
11 int h[N],p[N],ans[N];
12 //h[i]:前i位字符串的hash值
13 //p[i]:BASE^i
14 //ans[i]:记录hash值为i的子串的个数 
15 signed main(){
16     cin>>s>>k;
17     int len=s.size();
18     s=" "+s;
19     p[0]=1;h[0]=0;
20     for(int i=1;i<=len;i++){
21         h[i]=(h[i-1]*BASE+s[i]-'A')%MOD;//hash值的计算
22         p[i]=(p[i-1]*BASE)%MOD;
23     }
24     for(int l=1;l+k-1<=len;l++){
25         now=((h[l+k-1]-p[k]*h[l-1])%MOD+MOD)%MOD;//计算[l,l+k-1]的hash值,前缀和的思想
26         ans[now]++;
27         maxn=max(maxn,ans[now]);
28     }
29     cout<<maxn;
30     return 0;
31 }

 

posted @ 2021-07-09 16:11  爆零王  阅读(318)  评论(0)    收藏  举报