哈希表【散列表】详解

2023-11-14


哈希表【hash】

一.哈希表的插入及查询

hash表是一种数据结构,又称为散列表,其根本的原理就是把一个数变成另外一个易于存储的数。

先来看一道例题吧

假如有n个数,n的范围只有10万,但是每个数的大小有1e9,怎么做才能统计每个数出现的次数呢?

如果每个数的范围很小,则可以开一个数组(俗称为“桶”)存储。

但是 现在,根本开不了1e9的数组。而且,总共就1e5个数字,如果开1e9的数组,那么至少会浪费500000000个位置,就太不值得了。

事实上,根本没必要开那么大,只要能存1e5个数字就可以了。

于是我们就要把大小为1e9的数字映射到1e5

这就是hash的应用了。

具体映射的方法有很多,但最重要的就是取模法.

显而易见,只需要把每一个数模上一个1e5左右的数,就可以了。

如果H表示哈希函数,那么这个式子就是

H ( x ) = x % mod

一般情况下,模的这个数一般是一个质数。

这是为什么呢?

因为不同的两个数取模可能会得到同一结果,例如13和25,模数为12,则取模的结果都是1,存储时就会覆盖掉。

这被称为哈希冲突。

因此,摸一个质数可以减少冲突的概率,具体的原因可以搜一下。

不过这是尽可能的减少,并不能避免,所以,接下来介绍两种解决哈希冲突的方法。

1:拉链法

将所有取模后结果相同的都放在一个单链表里,查询时直接在单链表里查就行了。

盗一下图

类似于邻接表(链式前向星)。

不过我个人更倾向于第二种

2:开放地址法

通俗的来说,其实就是模过之后,如果这个位置已经有数字,那么就加上一个数,再取模,一直重复,直到找到空位。

也就是

H(x)=(x%mod+k)%mod

代码实现插入x

void Insert(int x)
{
	int p=H(x);
	while(hash[p]&&(hash[p]!=x)) p=(113+p)%10007;	
    hash[p]=x;
}

查询是否存在x

bool Query(int x)
{
	int p=H(x);
	while(hash[p]&&(hash[p]!=x)) pce=(113+p)%10007;	
	if(hash[p]!=0) return 1;
    return 0;
}

这也很好理解,当找到一个空位时,如果插入时有这个元素那么肯定会插入到空位中,因此之前没有插入过这个元素。

如果跳的时候直接找到了,就返回真即可。

二.哈希表的应用

1.随机的点

题目描述

陶陶为了给一道平面毒瘤计算几何题出数据,需要产生 N 个点(x[i],y[i])。已知x,y是由伪随机函数顺序产生,即:

X[i+1]=(X[i]∗Ax+Bx+i)%Cx(X[1],Ax,Bx,Cx是事先给定的)

Y[i+1]=(Y[i]∗Ay+By+i)%Cy(Y[1],Ay,By,Cy是事先给定的)

这样,就可以快速连续产生很多点坐标(X[i], Y[i])。 不幸的是,这样产生的点有可能有相同的,虽然这种几率很少,但严谨的陶陶不允许这种事发生。陶陶要求你帮助他解决最少要产生前多少项时,正好有 N 个不相同的点。

输入格式

第一行。一个整数 N .

第二行:4个整数X[1],Ax,Bx,Cx

第三行:4个整数Y[1],Ay,By,Cy

输出格式

一个整数 M 。表示最少要连续产生 M 个点,正好有 N 个不相同的点。数据保证有答案。

样例数据

input

21
2 4 3 6
5 2 3 13

output

24(原始样例为25,请大家实验)

数据规模与约定

1<=N<=1,000,000, 其它所有数据都在[1...1,000,000,000]范围内。

时间限制:1texts

空间限制:256textMB

题解

本题的关键在于如何存储两个值。

于是我们可以把这对坐标用类似于K进制存起来,再进行哈希。

long long Trydent(long long x,long long y)
{
	return (x*131%200351+y*131*131%2000351)%2000351;
}

完整代码如下

#include<bits/stdc++.h>
using namespace std;
long long s,k;
struct node
{
	long long x,y,v;
}pce[5000100];
long long HASH(long long x)
{
	return (x+3045173)%2323237;
}
long long Trydent(long long x,long long y)
{
	return (x*131%200351+y*131*131%2000351)%2000351;
}
long long n,sum=1,cnt=1;
long long x,y,a1,b1,c1,a2,b2,c2;
int main()
{
	freopen("distinct.in","r",stdin);
	freopen("distinct.out","w",stdout);
	cin>>n;
	cin>>x>>a1>>b1>>c1;
	cin>>y>>a2>>b2>>c2;
	while(sum<n)
	{
		x=(1ll*x*a1+b1+cnt)%c1;
		y=(1ll*y*a2+b2+cnt)%c2;		
		s=HASH(Trydent(x,y));
		if(pce[s].v==0)
		{
			pce[s].v=1;
			pce[s].x=x;
			pce[s].y=y;
			sum++;
		}	
		else 
		{
			k=HASH( Trydent( pce[s].x , pce[s].y ) );
			while(pce[k].v&&(pce[k].x!=x||pce[k].y!=y))
			{
				k=HASH(k);
			}			
			if(pce[k].v==0)
			{
				sum++;
				pce[k].v=1;
				pce[k].x=x;
				pce[k].y=y;
			}	
		}
		cnt++;	
	}
	cout<<cnt;
	return 0;
}

不过在更多的题中,哈希只是一种辅助,并不是主要的。

2.洛谷P6273 [eJOI2017]魔法

题目描述

给定一个长度为 n 的字符串 S。设 S 中不同的字符数为 k 。

定义字符串的子串为该字符串某一连续段。

而 有魔法的子串 被定义为 S 的某一非空子串,满足该子串中不同的字符数为 k ,且每个字符的出现的次数都相同

你需要求出给定字符串 S 的不同的 有魔法的子串 的个数。

若两个子串的左右端点不同,则这两个子串不同。

输入格式

第一行:一个整数 n 表示字符串长度。

第二行:一个字符串 S 。

输出格式

一个整数表示答案 mod(1e9+7) 的值。

输入输出样例

输入 #1复制

8
abccbabc

输出 #1复制

4

输入 #2复制

7
abcABCC

输出 #2复制

1

输入 #3复制

20
SwSSSwwwwSwSwwSwwwwS

输出 #3复制

22

说明/提示

【输入输出样例解释】

样例 1 解释

  • 满足条件的子串有: abc,cba,abc,abccba

样例 2 解释

  • 仅子串abcABC 为 有魔法的子串(区分大小写,即 a不等于A)。

样例 3 解释

  • 其中一个是 SwSwwS。

【数据规模与约定】

本题采用多测试点捆绑测试,共有 4 个子任务

  • Subtask 1(10 points):2≤n≤100。
  • Subtask 2(20 points):2≤n≤2×1e3。
  • Subtask 3(30 points):2≤n≤1e5,k=2 (即 S 中只有两种字符)。
  • Subtask 4(40 points):无其他限制。

对于所有数据,保证 2≤n≤1e5,字符集为[a,z]∪[A,Z]

【说明】

原题来自:eJOI 2017 Problem A Magic

翻译提供:@_Wallace_

题解

如果设sum[i][ch]表示1~i 的字符中有多少个字符ch。

则很明显 题中的子串满足 所有的sum[i][ch]-sum[j-1][ch]都相等

例如 sum[i][a]-sum[j-1][a]=sum[i][b]-sum[j-1][b]

则 sum[i][a]-sum[i][b]=sum[j-1][a]-sum[j-1][b]

换句话说这段区间的两个端点任意两种字符个数对应的差都是相等的。

因此我们可以处理出sum数组,然后用相邻的作差,一共k-1个就可以了,然后用类似第一题的方法哈希出来。当两个位置的哈希值完全相同时,则这个子串是有魔法的。

for(int i=1;i<=n;i++)
{
	for(int j=1;j<=k;j++)
	sum[i][ch[j]]=sum[i-1][ch[j]];//ch表示第i种字符
	sum[i][s[i]]++;
	for(int j=1;j<k;j++)
	New[i][j]=sum[i][ch[j]]-sum[i][ch[i+1]];
} 

本题不提供完整代码,嘿嘿。

三.字符串哈希及其应用

字符串哈希,就是把字符串转换成一个数字存起来。其思想就是把字符串看成一个K进制的数,也就是a=1,b=2,c=3……z=26,然后再取模

只不过为了避免低效的取模运算,通常用unsigned long long 存储,溢出时会自动取模

一般,我们的K取131或13331,此时哈希冲突产生的概率极低(几乎没有)。

这样我们就可以得到一个字符串的哈希值

那么接着思考,如果已经知道了字符串S的hash值H(S),T的hash值H(T),那么字符串S+T的哈希值能否求出呢?

H(S+T)=(H(S)*K^{lengh(T)}+H(T))%vmod

同理

H(T)=(H(S+T)-H(S)*K^{lengh(T)})% mod

通过这两种操作,我们可以预处理一个字符串的所有前缀字符串的hash值并通过第二种操作得到每一段字符的hash值

 兔子与兔子

题目描述

很久很久以前,森林里住着一群兔子。有一天,兔子们想要研究自己的 DNA 序列。我们首先选取一个好长好长的 DNA 序列(小兔子是外星生物,DNA 序列可能包含 26 个小写英文字母),然后我们每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两个兔子是否一模一样。注意两个兔子一模一样只可能是他们的 DNA 序列一模一样

输入和输出

Input

第一行一个 DNA 字符串 S。 接下来一个数字 m,表示 m 次询问。 接下来 m 行,每行四个数字 l1, r1, l2, r2,分别表示此次询问的两个区间,注意字符串的位置从1开始编号。 其中 1 ≤ length(S), m ≤ 1000000

Output

对于每次询问,输出一行表示结果。如果两只兔子完全相同输出 Yes,否则输出 No(注意大小写)

样例

Sample Input

aabbaabb
3
1 3 5 7
1 3 6 8
1 2 1 2

Sample Output

Yes
No
Yes

数据规模与约定

时间限制:1s

空间限制:256MB

很简单,只要用刚才的代码就能求出每一段的hash值,当两段hash相同时就视为相同

#include<bits/stdc++.h>
using namespace std;
unsigned long long f[1000020],p[1000200],a,b;
char c[1000200];
int l1,l2,r1,r2,s,n;
int main()
{
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout); 
	scanf("%s",c+1);
	s=strlen(c+1);
	p[0]=1;
	for(int i=1;i<=s;i++)
	{
		f[i]=f[i-1]*131+(c[i]-'a'+1);
		p[i]=p[i-1]*131;
	}
	scanf("%d",&n);
	while(n--)
	{
		scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
		if(f[r1]-f[l1-1]*p[r1-l1+1]==f[r2]-f[l2-1]*p[r2-l2+1]) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
} 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

哈希表【散列表】详解 的相关文章

  • set 中的哈希表在 python 中如何工作?

    据我所知 set在python中通过哈希表来实现O 1 查找复杂度 虽然它是哈希表 但其中的每个条目set必须是可散列的 或不可变的 所以这种和平的代码引发了异常 gt gt gt dict Traceback most recent ca
  • 哈希链接重新加载页面

    我有一个安装在第三方网站上的代码片段 我无法了解详细信息 但它通过使用 a 将 HTML CSS 和 JS 加载到页面上
  • 为什么 Java 中的 hashCode() 可以对不同对象返回相同的值?

    引用我正在读的书中的一段话首先Java http www amazon co uk Head First Java Kathy Sierra dp 0596009208 关键是 哈希码可以相同 但不一定保证对象相等 因为使用的 哈希算法 h
  • 如何对定义的字符集python中的所有可能的字符串进行加密?

    我试图加密定义的字符集中所有可能的字符串 然后将它们与用户输入给出的哈希进行比较 这就是我目前拥有的 import string from itertools import product import crypt def decrypt
  • 如何使redis中的“HSET”子键“过期”?

    我需要使 Redis 哈希中所有超过 1 个月的密钥过期 这不可能 https github com antirez redis issues 167 issuecomment 2559040 为了保持 Redis 简单 https git
  • 为什么即使我的哈希码值相同,“==”也会返回 false

    我写了一个像这样的课程 public class HashCodeImpl public int hashCode return 1 public static void main String args TODO Auto generat
  • 数学组合的完美最小哈希

    首先定义两个整数N and K where N gt K 两者都在编译时已知 例如 N 8 and K 3 接下来 定义一组整数 0 N or 1 N 如果这使答案更简单 并调用它S 例如 0 1 2 3 4 5 6 7 的子集数量S wi
  • JSON 中的哈希到底是什么?

    我正在学习 JSON 但我发现你也可以将所谓的 哈希 放入 JSON 中 我在哪里可以找到什么是哈希 或者你能向我解释一下什么是哈希吗 另外 什么是哈希图 我有 C 和 C 经验 正在学习 JS Jquery 和 JSON 哈希是一个稀疏数
  • python的hash()是持久的吗?

    Is the hash python 中的函数保证对于给定的输入始终相同 无论输入的时间 地点如何 到目前为止 仅从反复试验来看 答案似乎是肯定的 但最好了解其工作原理的内部原理 例如 在测试中 python gt gt gt from i
  • 按值和键对哈希进行排序(按顺序)

    我正在寻找一种很好的方法来在 Perl 中先按值排序 然后再按键排序 Example my userids williams gt Marketing smith gt Research johnson gt Research jones
  • 如何使用“子例程引用”作为哈希键

    在 Perl 中 我正在学习如何取消引用 子例程引用 但我似乎无法使用子例程引用作为哈希 键 在下面的示例代码中 我可以创建对子例程 subref 的引用 然后取消引用它以运行子例程 subref 我可以使用引用作为哈希 值 然后轻松取消引
  • javascript:完全删除top.location.hash?

    如果我的地址栏中已经有一个哈希值 例如domain com whatever 我打电话 top location hash wathever 被转换为domain com 没有任何内容 是否可以完全删除哈希值 所以没有 left 因为如果我
  • 根据哈希值确认文件内容

    我需要 检查完整性 content文件数量 文件将写入 CD DVD 可能会被复制多次 这个想法是识别正确复制的副本 在从 Nero 等中删除它们之后 我对此很陌生 但快速搜索表明Arrays hashCode byte http down
  • c# AudioFingerprinting 和局部敏感哈希

    我之前发现过类似的帖子 但没有真正回答这个问题 在我的指纹识别中 我生成了一个包含 5 个整数的记录集 例如 33 42 88 121 194 这些对应于特定音乐样本的最高幅度的频率 例如 对于 30ms 的音频样本 我有以下频率的桶 0
  • 我仍然认为在客户端哈希密码更好。我错了吗?

    我读过这些 https hackernoon com im harvesting credit card numbers and passwords from your site here s how 9a8cb347c5b5 https
  • NodeJS“加密”哈希似乎产生与 Crypto-JS javascript 库不同的输出

    我正在使用 NodeJS 的捆绑包crypto http nodejs org api crypto html crypto class hash服务器端 SHA256 哈希模块 在客户端 我使用一个名为的 javascript 库Cryp
  • 与 6 位随机字母数字代码发生冲突的概率是多少?

    我使用以下 Perl 代码生成随机字母数字字符串 仅限大写字母和数字 用作 MySQL 数据库中记录的唯一标识符 数据库的行数可能会保持在 1 000 000 行以下 但实际的绝对最大值约为 3 000 000 行 我是否有 2 条记录具有
  • MD5 哈希怎么可能无法“解密”呢? [复制]

    这个问题在这里已经有答案了 可能的重复 为什么 MD5 哈希值不可逆 https stackoverflow com questions 330207 how come md5 hash values are not reversible
  • 在 Perl 中将整个文件读入哈希值

    我在 Perl 中将文件读入哈希时遇到一些问题 Chr1 supercontig 000000000 1 500 PILOT21 588 1 3 14602 59349 1 Chr1 supercontig 000000001 5 100
  • 使用 openssl 库获取 x509 证书哈希

    我目前正在开发一个应用程序 它使用 openssl 库 libcrypto 来生成证书 现在我必须获取现有证书的哈希值 当我使用终端时 我可以使用以下命令生成哈希值 openssl x509 hash in cert pem noout 输

随机推荐

  • CSP 202305-3 解压缩

    这道T3主要是能够读懂题目 然后根据题意进行模拟 需要比较多的位运算 在此题 我选择用uint8 t存储一个字节 然后对字符和数字进行了转换 include
  • Java多线程-锁的概念

    1 结婚戒指的意义 根据文献记载 最早使用戒指人就是希腊的悲剧英雄 被缚的普罗米修斯 宙斯为惩罚普罗米修斯盗火给人类 将他绑缚在考卡苏斯山上 每天都有一只老鹰飞到山上 将他的内脏啄出 到了夜晚 他所失去的器官又会重新长出来 后来 大力士海格
  • Kubernetes 中部署 NFS Provisioner 为 NFS 提供动态分配卷

    Kubernetes 中部署 NFS Provisioner 为 NFS 提供动态分配卷 文章目录 Kubernetes 中部署 NFS Provisioner 为 NFS 提供动态分配卷 一 NFS Provisioner 简介 二 创建
  • bert-as-service配置

    环境配置 conda create n bert service python 3 8 conda activate bert service pip install user nvidia pyindex pip install user
  • 11.Java数据库连接

    Java数据库连接 概念 在软件开发中 经常需要与数据库进行交互来存储和检索数据 Java提供了一种称为JDBC Java Database Connectivity 的API 用于连接和操作各种类型的关系型数据库 数据库连接是指通过Jav
  • 卷积与傅立叶变换

    一 卷积 1 一维的卷积 连续 在泛函分析中 卷积是通过两个函数 f x f x f x 和 g x g x g x 生成第三个函数的一种算子 它代表的意义是 两个函数中的一个 我取 g x
  • 私人网站域名服务器公安备案指南【网站备案】

    今天收到了工信部的审核通过短信 你的服务器要想使用域名解析 其中一个要求就是服务器要有备案 很开心 但 事情没那么简单 要求你30天内进行公安备案 我打开谷歌网址 开始了我的坎坷备案之旅 一天下午 加一天晚上 第二天下午
  • vue —— 路由 replace

    作用 控制路由跳转时操作浏览器历史记录的模式 2 浏览器的历史记录有两种方式 分别为 push 和 replace push是追加历史记录 replace是替换当前记录 路由跳转的时候默认 push 3 开启replace模式
  • CPU使用率和负载Load计算方法

    2019独角兽企业重金招聘Python工程师标准 gt gt gt Loadavg分析 Loadavg浅述 cat proc loadavg 可以看到当前系统的load cat proc loadavg 0 01 0 02 0 05 2 3
  • 判断两条线段是否相交(C++)

    背景 在做51nod上的第1951题时 需要根据给出的两条线段来判断这两条线段是否相交 所以在这里记录一下 判断两条线段是否相交有两步 快速排斥计算 跨立计算 快速排斥 给出线条AB CD 如果以AB CD为对角线的矩形不相交 那么AB C
  • mysql5.5安装最后卡主_在MySQL5.5版本时安装到最后一步卡死的解决办法

    今天给老师安装mysql 5 5 版本时出了问题 老师的电脑为windows7 mysql安装版本为mysql 5 5 安装到最后一步 mysql实例配置最后一步卡死了 安装了多次也没有方法 百度了许多方法 比如删除注册表 删除某些主要文件
  • 量化交易项目怎么做

    同学们前面两期量化交易内容 Python量化交易入门 量化交易的历史 文章目录 学习目标 1 量化交易研究流程 1 1 分析结果 1 2 什么是策略 1 3 流程包含的内容 二 量化开发和研究岗位的要求 学习目标 1 说明量化交易的研究流程
  • SourceTree 解決冲突 Please commit your changes or stash them before you merge.

    目录 本机环境 通过 Sourcetree 拉取代码时提示冲突信息 一 本机环境 本机系统 Mac git 2 24 1 git version 查看 二 通过 Sourcetree 拉取代码时提示冲突信息 原因 同分支下 其他伙伴修改的
  • @SpringBootApplication失效问题

    SpringBootApplication的默认扫描范围 在练习springsecurity中 突然发现spring的自动配置失效 需要手动的在主应用程序类添加 ComponentScan才可以扫描到 然后排查发现idea自动在根路径创建一
  • 提示工程师指南4-ChatGPT Prompt Engineering

    ChatGPT Prompt Engineering 在这个部分 我们将介绍 ChatGPT 的最新提示工程技术 包括技巧 应用 限制 论文和额外的阅读材料 主题 与 ChatGPT 对话 Python 笔记本 请注意 本部分正在紧密开发中
  • java中的service层,dao层,controller层的理解

    基本概念 DAO层 DAO层叫数据访问层 属于一种比较底层 比较基础的操作 具体到对于某个表的增删改查 也就是说某个DAO一定是和数据库的某一张表一一对应的 其中封装了增删改查基本操作 Service层 Service层叫服务层 被称为服务
  • WORD文档误删除、误清空等恢复的几种方法

    前因 word中保存了近一个星期的读书笔记 设置了自动保存 也会习惯性的CTRL S手动保存 但前天word不知怎么就挂了 再打开时写的文档已经不在本地文件夹了 当时就傻眼了 刚开始只好认栽就打算重新录一遍吧 但越想越觉得浪费时间 觉得肯定
  • Python 中的异常处理

    异常的原因通常在程序本身之外 例如 不正确的输入 输入输出设备故障等 由于程序在遇到异常时会突然终止 因此可能会对系统资源 如文件 造成损害 因此 应该正确处理异常 以防止程序突然终止 Python 使用try和except关键字来处理异常
  • 牛客网刷题-两数之和

    问题描述 给出一个整数数组 请在数组中找出两个加起来等于目标值的数 你给出的函数twoSum 需要返回这两个数字的下标 index1 index2 需要满足 index1 小于index2 注意 下标是从1开始的 假设给出的数组中只存在唯一
  • 哈希表【散列表】详解

    哈希表 hash 一 哈希表的插入及查询 hash表是一种数据结构 又称为散列表 其根本的原理就是把一个数变成另外一个易于存储的数 先来看一道例题吧 假如有n个数 n的范围只有10万 但是每个数的大小有1e9 怎么做才能统计每个数出现的次数