哈希冲突 及 双哈希
前言
若不知道 哈希和字符串哈希 看我上一个作品:
字符串哈希从入门到精通
今天讲的约等于它的拓展……
一.解决哈希冲突
1.1 基本概念
字符串哈希 将不同的字符串映射成不同的整数。
思想:将字符串映射成一个 p进制数字。
我们定义如下哈希函数:(该字符串下标从1开始)
例如:p = 131,s = abc,其哈希值为
但是,有时会存在多个不同的字符串哈希值相同的情况,我们通常的处理方式是调整p和M的值,取p为质数,M为大质数。
M 我们要取一个比较大的质数,但是可恶的出题人往往对一些质数如1e9 + 7、998244353等搞一些卡哈希的数据
(恶心)(往往通过生成一个随机数,根据随机数再取质数,去避免卡hash)
当然我们肯定是有解决方案了,那就是我们今天要讲的双哈希!!!(其实还有许多方法但考试一般只需要双哈希就够了,如果在意可以去了解一下)
双哈希有多强呢?有一道题叫 “Hash Killer III” 题目大概就是让你造数据去卡他的双哈希,直到出题网站倒闭这个题也没人A……
接下来给大家带来方法……
1.2 实现方式
双哈希顾名思义就是模两次hash 防止出题人的狡猾
整理了一个模板(仅供参考)
struct HASH{
long long sed,mod,h[N],pw[N];
void init(int ser_in,int mod_in){
sed=ser_in,mod=mod_in;
pw[0]=1;
for(int i=1;i<N;i++){
pw[i]=pw[i-1]*sed%mod;
}
}
void make(string s){
h[0]=s[0]%mod;
for(int i=1;i<s.size();i++){
h[i]=(h[i-1]*sed%mod+s[i])%mod;
}
}//构造hash
long long get(int l,int r){
return (h[r]-h[l-1]*pw[r-l+1]%mod+mod)%mod;
}//取子串可以根据题目使用
}S1,S2;
该怎么用呢?来个题试试!!!
二.例题《不同子串》
2.1 题目描述
2.2 思路
我们输入完后建立一个pair数组去记录我们的双哈希,然后将数组排序方便下面比较,比较这一个的哈希值与前一个的哈希值(我们已经排序了所以前面的要么比他小,要么相等)是否相等,如果不相等或i==1,那么不同的字串+1
2.3 代码
#include<bits/stdc++.h>
using namespace std;
string s;
int n,l;
const int N=1e6+5;
struct HASH{
long long sed,mod,h[N],pw[N];
void init(int ser_in,int mod_in){
sed=ser_in,mod=mod_in;
pw[0]=1;
for(int i=1;i<N;i++){
pw[i]=pw[i-1]*sed%mod;
}
}
void make(string s){
h[0]=s[0]%mod;
for(int i=1;i<s.size();i++){
h[i]=(h[i-1]*sed%mod+s[i])%mod;
}
}
long long get(int l,int r){
return (h[r]-h[l-1]*pw[r-l+1]%mod+mod)%mod;
}
}S1,S2;//模板
int idx;
pair<long long,long long> ans[N];//双哈希数组
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int cnt=0;
cin>>n>>l;
cin>>s;
s=" "+s;
S1.init(114,1e9+7);
S2.init(114514,998244353);//构造
S1.make(s);
S2.make(s);//构建整个字符串的哈希
for(int i=1;i<=n-l+1;i++){
ans[++idx].first=S1.get(i,i+l-1);
ans[idx].second=S2.get(i,i+l-1);//截取字串
}
sort(ans+1,ans+idx+1);
for(int i=1;i<=idx;i++){
if(ans[i]!=ans[i-1]||i==1){//判断是否为不同字串
cnt++;
}
}
cout<<cnt;//输出
return 0;
}