哈希(HASH) 学习笔记
提示:本篇文章仅仅针对普及组的OIer,并且,这是网上少有的一篇关于介绍哈希代码的文章
注:提高组的大佬们勿喷。
板子题
题目描述
如题,给定个字符串(第i个字符串长度为,字符串内包含数字、大小写字母,大小写敏感),请求出个字符串中共有多少个不同的字符串。
输入
第一行包含一个整数,为字符串的个数。
接下来行每行包含一个字符串,为所提供的字符串。
输出
输出包含一行,包含一个整数,为不同的字符串个数。
样例输入
5
abc
aaaa
abc
abcc
12345
样例输出
4
提示
时空限制:ms,
数据规模:
对于30%的数据:,,;
对于60%的数据:,,
对于100%的数据:N<=100000,Mi≈1000,Mmax<=1500
样例说明:
样例中第一个字符串(abc)和第三个字符串(abc)是一样的,所以所提供字符串的集合为{aaaa,abc,abcc,12345},故共计个不同的字符串。
算法理解
哈希,又称散列、杂凑,英文名为Hash。
哈希函数
哈希函数这个东西,一般是提高组的内容,对于普及组来说,哈希的内容如下:
- 一个数一般直接 一个大质数即可
- 一堆数全部乘起来然后在 一个大质数(如果有负数要平方,以免出现负下标),最好边乘边模,以免炸
- 字符串可以利用ASCII码来进行进制转换,但还是要 一个大质数,边乘边模,防炸。
- 至于图、书的哈希函数,普及组一般不会做要求
换一篇看看吧
可能进制转换大家还听不懂,看代码:
for(j=1;j<=s.size();j++) pos=(pos*100+s[j])%MOD;
这里就是哈希值,而 就是一个 类型的字符串。因为ASCII码的范围为32~128,所以这里乘上100就可以了。
当然,很多的地方都用到了Hash,比如说一款程序的密码,密码其实就是利用一种叫做 MD5码 的Hash函数来进行转换,并且产生哈希冲突的可能性为0,且无法破译(最多穷举),所以,我们 只能“重置密码”,而不能“找回密码”,就是这个原因。
哈希冲突
当我们取哈希函数为这样的时候:
pos=a%5;
这里就是哈希值,而 就是一个 类型的数字。那么当和的时候,的值都等于,那么该怎么办呢?其实这个就是哈希冲突。哈希的复杂度其实就在于哈希冲突的可能性,这就是要 一个大质数的原因。
设这一堆要哈希的数为
哈希函数:模
方法一:开放寻址法
方法思路:这一个放不下了,就放下一个呗。
状态 | |||||
---|---|---|---|---|---|
空 | |||||
进来 | |||||
进来 | |||||
进来 | |||||
进来 | |||||
我们发现,当当前的数字的位置已经有数字的时候,就会到下一个位置,直到有空位为止。这种方法空间小,速度慢 | |||||
代码(字符串): |
int HASH (int pos){//开放寻址法 s是输入的字符串 int cur=pos; while(!a[cur].empty()){ if(a[cur]==s) return 0;//已经找到 cur=(cur+1)%MOD; } a[cur]=s; return 1;//表示没有找到 }
方法二:挂链法
方法思路:新建一个链表,将每一个Hash之后相同的字符串放在同一个链表里面即可,使用一个数组来存放链表(节省空间,防MLE).
首先开一个数组。表示第个下标的链表头,表示没有,再开一个数组,表示第个数的下一个的地址,表示没有。添加的时候从头加。数组表示的是数据。表示现在储存到数组的哪一个下标了。
这种方法时间小空间大。
int HASH(int pos){//挂链法 int cur=head[pos]; while(cur!=-1){ if(a[cur]==s) return 0; cur=nex[cur]; } //空出0号位置,原因:未知 a[++k]=s; nex[k]=head[pos]; head[pos]=k; return 1; }
原题解析
其实,这题就是一道裸的哈希,并且也可以用map来做,但是,map其实就是哈希,只不过机带的要稍微慢一点。。。
哈希函数就用处理字符串的方法::进制法,然后不管是开放寻址法还是挂链法都无所谓了,但开放寻址法还是要稍微快一点。
AC代码(这就是个模板,希望大家能加上自己的码风,然后背下来)::
#include<cstdio> #include<iostream> #include<cstring> #define maxn 100039 #define MOD 1000039 using namespace std; int n,ans,i,j,pos,k; int head[MOD+39],nex[MOD+39]; string s,a[MOD+39]; int HASH(int pos){//挂链法 int cur=head[pos]; while(cur!=-1){ if(a[cur]==s) return 0; cur=nex[cur]; } //空出0号位置,原因:未知 a[++k]=s; nex[k]=head[pos]; head[pos]=k; return 1; } /*int HASH (int pos){//开放寻址法 int cur=pos; while(!a[cur].empty()){ if(a[cur]==s) return 0; cur=(cur+1)%MOD; } a[cur]=s; return 1; } */ int main(){ scanf("%d",&n); memset(head,-1,sizeof(head)); for(i=1;i<=n;i++){ cin>>s; pos=0; for(j=1;j<=s.size();j++) pos=(pos*100+s[j])%MOD; if(HASH(pos)) ans++; } printf("%d",ans); return 0; }
END.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具