哈希-

哈希表:https://oi-wiki.org/ds/hash/

目录

一、存储结构

1.开放寻址法

2.拉链法

二、字符串哈希


哈希表(一种期望算法):1.存储结构---1)开放寻址法  2)拉链法 xmodk k是一个质数且k远离2^n
    O(1)                  2.字符串哈希方式
    添加(h[x])、查找(h[x])、删除(一般不删,开一个额外的数组,标记要删除的数组)
    

一、存储结构

题目来源:Acwing---840-模拟散列表

题目描述:

维护一个集合,支持如下几种操作:

I x,插入一个数 x;
Q x,询问数 x 是否在集合中出现过;
现在要进行 N 次操作,对于每个询问操作输出对应的结果。

输入格式
第一行包含整数 N,表示操作数量。

接下来 N 行,每行包含一个操作指令,操作指令为 I x,Q x 中的一种。

输出格式
对于每个询问指令 Q x,输出一个询问结果,如果 x 在集合中出现过,则输出 Yes,否则输出 No。

每个结果占一行。

数据范围
1≤N≤105
−109≤x≤109
输入样例:
5
I 1
I 2
I 3
Q 2
Q 5
输出样例:
Yes
No

1.开放寻址法

开放寻址法:添加:取mod后如果有就往后找,有空的就存里面
                查找:取mod后,从前往后找,如果=x就找到,空的话就没有
                删除:一般不删,开一个额外的数组,标记要删除的数组

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
const int N=200003,null=0x3f3f3f3f;////N一般开两倍且为质数

int h[N];

int find(int x){	//如果x在哈希表中,返回x的位置;如果x不在哈希表中,返回x应该存储的位置
	int k=(x%N+N)%N;
	while(h[k]!=null&&h[k]!=x){
		k++;
		if(k==N)k=0;
	}
	return k;
}
int main(){
	int n;
	cin>>n;
	memset(h,0x3f,sizeof h);
	while(n--){
		char op[2];
		int x;	
		cin>>op>>x;
		int k=find(x);
		if(op[0]=='I')h[k]=x;
		else{
			if(h[k]!=null)puts("Yes");
			else puts("No");
		}
	}	
}

2.拉链法

#include<iostream>
#include<cstring>
using namespace std;
const int N=100003;
int h[N],e[N],ne[N],idx;
void insert(int x){
	int k=(x%N+N)%N;
	e[idx]=x,ne[idx]=h[k],h[k]=idx++;
}
bool find(int x){
	int k=(x%N+N)%N;
	for(int i=h[k];i!=-1;i=ne[i]){
		if(e[i]==x)
			return true;
	}
	return false;
}
int main(){
	int n;
	cin>>n;
	memset(h,-1,sizeof h);
	while(n--){
		char op[2];
		int x;	
		cin>>op>>x;
		if(op[0]=='I')insert(x);
		else{
			if(find(x))puts("Yes");
			else puts("No");
		}
	}	
}

二、字符串哈希

步骤:

1.把字符串看成p进制的数(可以把字符串转换成数字)
    eg:ABCD --- A:1 B:2 C:3 D:4    
2.把p进制的数转换成10进制的数    eg: ABCD=1*p^3+2*p^2+3*p^1+4*p^0
3.取mod    eg: ABCD=(1*p^3+2*p^2+3*p^1+4*p^0)%mod ---将ABCD映射成从0~mod-1的数


注意1.  一般情况下:不能把某个字母映射成0        --eg: a:0  则 aa=0、aaa=0

           2. 假定我们人品足够好,不存在冲突,有经验值,当p=131或者p=13331,mod取2^64,这种方式99.99%不会出现冲突。

以上哈希方式+前缀哈希的好处:可以利用前缀哈希计算出任意子串的哈希值
    eg: 有一个长度为n的字符串,已知h[r],h[l-1],求l~r的hash
        h[r]
        h[l-1]*p^(r-l+1)//左移,和h[r]对齐
        h[l~r]=h[r]-h[l-1]*p^(r-l+1)
        unsigned long long 存储h,unsigned long long会溢出,相当于mod2^64。

哈希字符串作用:可以快速判断两个字符串是否相同(如下)

题目来源:Acwing---841-字符串哈希

问题描述:

代码如下:

//字符串前缀哈希法
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ULL;
const int N=100010,P=131;
int n,m;
char str[N];
ULL h[N],p[N];//h[i]: 前i个字符的哈希值,前0个字符的哈希值是0
ULL get(int l,int r){
	return h[r]-h[l-1]*p[r-l+1];
}
int main(){
	scanf("%d%d%s",&n,&m,str+1);
	p[0]=1;
	for(int i=1;i<=n;i++){
		p[i]=p[i-1]*P;
		h[i]=h[i-1]*P+str[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");
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值