如果你已经确保自己的hash技巧已经入门,那么请左转这篇博文

首先介绍一下hash?

事实上是一种叫做蛤丝的病毒

以下讲到的hash都是OI中最常用到的hash方法:进制哈希

做法:

首先设一个进制数base,并设一个模数mod

而哈希其实就是把一个数转化为一个值,这个值是base进制的,储存在哈希表中,注意一下在存入的时候取模一下即可

比如说现在有一个字符串orzc

枚举这个字符串的每一位,与base相乘得到ans,然后mod一下,就得到orzc的哈希值

但是哈希有一个很大的弊端:

哈希冲突

什么是哈希冲突呢?

就比如说orzc的哈希值是233,而orzhjw的哈希值也是233

那么我们在查询的时候代码会认为这两个字符串是相同的,但显然这两个字符串是不同的

减少哈希冲突的方法很多

自然溢出法,双哈希之类的

看一道例题理解一下

洛谷P3370 【模板】字符串哈希

题目描述

如题,给定N个字符串(第i个字符串长度为Mi,字符串内包含数字、大小写字母,大小写敏感),请求出N个字符串中共有多少个不同的字符串。

友情提醒:如果真的想好好练习哈希的话,请自觉,否则请右转PJ试炼场:)

输入输出格式

输入格式:

第一行包含一个整数N,为字符串的个数。

接下来N行每行包含一个字符串,为所提供的字符串。

输出格式:

输出包含一行,包含一个整数,为不同的字符串个数。

输入输出样例

输入样例#1: 复制

5
abc
aaaa
abc
abcc
12345
输出样例#1: 复制

4

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=10,Mi≈6,Mmax<=15;

对于70%的数据:N<=1000,Mi≈100,Mmax<=150

对于100%的数据:N<=10000,Mi≈1000,Mmax<=1500

样例说明:

样例中第一个字符串(abc)和第三个字符串(abc)是一样的,所以所提供字符串的集合为{aaaa,abc,abcc,12345},故共计4个不同的字符串。

Tip: 感兴趣的话,你们可以先看一看以下三题:

BZOJ3097:http://www.lydsy.com/JudgeOnline/problem.php?id=3097

BZOJ3098:http://www.lydsy.com/JudgeOnline/problem.php?id=3098

BZOJ3099:http://www.lydsy.com/JudgeOnline/problem.php?id=3099

如果你仔细研究过了(或者至少仔细看过AC人数的话),我想你一定会明白字符串哈希的正确姿势的^_^

事实上如果理解了刚刚讲的hash的原理的话,这道题就很水了,因为本来就是模板题

用一段hash的代码再来巩固一下刚才的知识

#define base 233
#define inf 1<<30
ull mod=inf;
//定义一个大数(最好是质数)作为模数,这里用的是1<<30
//定义一个base进制,这里是233
il ull hash(char s[]){
ll ans=,len=strlen(s);
for(ll i=;i<len;i++){
ans=(base*ans+(ull)s[i])%mod;
}
return ans;
//枚举该字符串的每一位,与base相乘,转化为base进制,加(ull)是为了防止爆栈搞出一个负数,(ull)是无符号的,但其实加了一个ull是可以不用mod的,加个mod更保险
//然而加了mod会很玄学,莫名比不加mod慢了300多ms
}

因为懒就没有去找一个大质数来当mod,用了1<<30代替,但是最好还是找一个大质数当mod(搜索一下生日悖论?大概就会明白原因了)

最后贴一下刚刚的例题的两种解法:

解法1:单hash/自然溢出法

这里就当一种解法来说吧

因为代码差异不大

这道题的话单hash mod开大质数是可以过的,但是在大多数难一些的题目里面是会被卡掉的

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll int
#define inf 1<<30
#define mt(x,y) memset(x,y,sizeof(x))
#define il inline
#define ull unsigned long long
il ll max(ll x,ll y){return x>y?x:y;}
il ll min(ll x,ll y){return x<y?x:y;}
il ll abs(ll x){return x>?x:-x;}
il ll swap(ll x,ll y){ll t=x;x=y;y=t;}
il void read(ll &x){
x=;ll f=;char c=getchar();
while(c<''||c>''){if(c=='-')f=-f;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
x*=f;
}
using namespace std;
#define N 10001
#define base 233
ull mod=212370440130137957ll;
ll f[N],n;
char a[N];
//ull hash(char s[]){ ll ans=0,len=strlen(s); for(ll i=0;i<len;i++){ ans=((base*ans+(ull)s[i])+mod)%mod; } return ans; }
//这个是单hash+大质数mod,也是可以过的,但是会比较慢
ull hash(char s[]){//自然溢出
ull ans=,len=strlen(s);
for(ll i=;i<len;i++){
ans=base*ans+(ull)s[i];
//这里不使用mod让它自然溢出,定义为ull的数在超过2^32的时候会自然溢出
//如果把这个换成上面的hash就会400ms+
//所以说自然溢出大法好
}
return ans;
}
int main(){
read(n);
for(ll i=;i<=n;i++){
scanf("%s",a);
f[i]=hash(a);
}
sort(f+,f+n+);ll ans=;
for(ll i=;i<n;i++){
if(f[i]!=f[i+])ans++;
}
printf("%d\n",ans);
return ;
}

解法2:双hash

其实就是用两个不同的mod来算hash,哈希冲突的概率是降低了很多,不过常数大,容易被卡,这道题要700ms+

本人还是更推荐自然溢出法

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll int
#define inf 1<<30
#define mt(x,y) memset(x,y,sizeof(x))
#define il inline
#define ull unsigned long long
il ll max(ll x,ll y){return x>y?x:y;}
il ll min(ll x,ll y){return x<y?x:y;}
il ll abs(ll x){return x>?x:-x;}
il ll swap(ll x,ll y){ll t=x;x=y;y=t;}
il void read(ll &x){
x=;ll f=;char c=getchar();
while(c<''||c>''){if(c=='-')f=-f;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
x*=f;
}
using namespace std;
#define N 10001
#define base 233
ull mod1=212370440130137957ll;
ull mod2=inf;
ll n;
char a[N];
struct node{ll x,y;}f[N];
il ull hash1(char s[]){
ll ans=,len=strlen(s);
for(ll i=;i<len;i++){
ans=(base*ans+(ull)s[i])%mod1;
}
return ans;
}
il ull hash2(char s[]){
ll ans=,len=strlen(s);
for(ll i=;i<len;i++){
ans=(base*ans+(ull)s[i])%mod2;
}
return ans;
}
il bool cmp1(node a,node b){return a.x<b.x;}
il bool cmp2(node a,node b){return a.y<b.y;}
int main(){
read(n);
for(ll i=;i<=n;i++){
scanf("%s",a);
f[i].x=hash1(a);
f[i].y=hash2(a);
}
sort(f+,f+n+,cmp1);sort(f+,f+n+,cmp2);
ll ans=;
for(ll i=;i<n;i++){
if(f[i].x!=f[i+].x||f[i].y!=f[i+].y)ans++;
}
printf("%d\n",ans);
return ;
}

这道题也是可以打字典树的,也是裸的做法,读者也可以尝试一下,因为这里是讲hash的所以就不放字典树的代码了

hash详解的更多相关文章

  1. 探索c#之一致性Hash详解

    阅读目录: 使用场景 算法原理 虚拟节点 代码示例 使用场景 以Redis为例,当系统需要缓存的内容超过单机内存大小时,例如要缓存100G数据,单机内存仅有16G时.这时候就需要考虑进行缓存数据分片, ...

  2. location.hash详解

    一.#的涵义 #代表网页中的一个位置.其右面的字符,就是该位置的标识符.比如, http://www.example.com/index.html#print 就代表网页index.html的prin ...

  3. location.hash 详解

    前年9月twitter改版. 一个显著变化,就是URL加入了"#!"符号.比如,改版前的用户主页网址为 http://twitter.com/username 改版后,就变成了 h ...

  4. redis详解(四)-- 高可用分布式集群

    一,高可用 高可用(High Availability),是当一台服务器停止服务后,对于业务及用户毫无影响. 停止服务的原因可能由于网卡.路由器.机房.CPU负载过高.内存溢出.自然灾害等不可预期的原 ...

  5. redis 五种数据结构详解(string,list,set,zset,hash)

    redis 五种数据结构详解(string,list,set,zset,hash) Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存 ...

  6. $.ajax()方法详解 ajax之async属性 【原创】详细案例解剖——浅谈Redis缓存的常用5种方式(String,Hash,List,set,SetSorted )

    $.ajax()方法详解   jquery中的ajax方法参数总是记不住,这里记录一下. 1.url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. 2.type: 要求为Str ...

  7. redis 五种数据结构详解(string,list,set,zset,hash),各种问题综合

    redis 五种数据结构详解(string,list,set,zset,hash) https://www.cnblogs.com/sdgf/p/6244937.html redis 与 spring ...

  8. 多表连接的三种方式详解 hash join、merge join、 nested loop

    在多表联合查询的时候,如果我们查看它的执行计划,就会发现里面有多表之间的连接方式.多表之间的连接有三种方式:Nested Loops,Hash Join 和 Sort Merge Join.具体适用哪 ...

  9. 【Redis】redis 五种数据结构详解(string,list,set,zset,hash)

    redis 五种数据结构详解(string,list,set,zset,hash) Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存 ...

随机推荐

  1. nginx日志切割配置

    编辑虚拟主机文件 /etc/nginx/conf.d/default.conf  在server段添加如下配置 if ($time_iso8601 ~ "^(\d{4})-(\d{2})-( ...

  2. C# MVC NPOI导出

    前台: <form id="fmexp" method="post" target="_blank"> </form> ...

  3. js通过a链接控制多个DIV只显示其中一个其它隐藏

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  4. java中的异常以及 try catch finally以及finally的执行顺序

    java中的 try.catch.finally及finally执行顺序详解: 1.首相简单介绍一下异常以及异常的运行情况: 在Java中异常的继承主要有两个: Error和Exception 这两个 ...

  5. 游戏安全有多重要?——GAME-TECH游戏开发者技术沙龙

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由云+社区运营团队发布在腾讯云+社区 腾讯云GAME-TECH沙龙继3月深圳站后,将于4月13日来到北京站,与游戏厂商和游戏开发者,畅聊 ...

  6. Beta冲刺合集

    Beta冲刺序列: Beta凡事预则立 :Beta No.0 Beta冲刺Day1:Beta No.1 Beta冲刺Day2:Beta No.2 Beta冲刺Day3:Beta No.3 Beta冲刺 ...

  7. c语言-第零次作业

    1.你认为大学的学习生活.同学关系.师生应该是怎样?请一个个展开描述. 我很荣幸能考进集美大学.集美大学历史悠久.师资力量雄厚.教师与学生素质高.并且集美大学的学习生活和我理想中的一样!首先老师认真负 ...

  8. Build to win--来自小黄衫

    写在前面 首先非常荣幸.非常侥幸能以微弱的优势得到这次小黄衫,感谢各位老师同学的帮助,也谢谢来自<构建之法>团队的小黄衫赞助! 这次能够获得小黄衫,就像汪老师上课说的那样,其实,是一个积累 ...

  9. 20162328蔡文琛week06

    学号 2016-2017-2 <程序设计与数据结构>第X周学习总结 教材学习内容总结 继承是从已有类派生出一个新类的过程. 继承的目的之一之复用已有的软件. 继承呢在子类和父类见建立了is ...

  10. JAVA使用和操作properties文件

    java中的properties文件是一种配置文件,主要用于表达配置信息,文件类型为*.properties,格式为文本文件,文件的内容是格式是"键=值"的格式,在properti ...