如果你已经确保自己的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. 使用 js 设置组合快捷键,支持多个组合键定义,还支持 React

    ╭┈┈╮ ╭┈┈╮ ╭┈┈╮ ┆ ├┈┈..┈┈┈┈┈.┆ └┈╮┆ ├┈┈..┈┈┈┈┈..┈┈.┈┈..┈┈┈┈┈. ┆ ┆┆ □ ┆┆ ┈┤┆ < ┆ -__┘┆ ┆ ┆┆__ ┈┈┤ ╰ ...

  2. 三十天学不会TCP,UDP/IP网络编程 -- TCP中的智慧之连续ARQ

    突然发现上一篇文章贴图有问题,关键我怎么调也调不好,为了表达歉意,我再贴一篇gitbook上的吧,虽然违背了我自己的隔一篇在这里发一次的潜规则~其余完整版可以去gitbook(https://www. ...

  3. linux设置代理修改接口数据

    其实很简单,希望看到的人可以一次搞定,所以我把所有步骤写一块儿了. 1.首先在自己能上网的机器上安装fiddler,程序自己百度搜就可以,百度软件中心的程序就行. 2.安装fiddler后,管理员权限 ...

  4. C语言中数据类型的取值范围

    C语言中数据类型的取值范围如下:char -128 ~ +127 (1 Byte)short -32767 ~ + 32768 (2 Bytes)unsigned short 0 ~ 65536 (2 ...

  5. (译文)开始学习Webpack-应用TypeScript,配置热加载和Source Map

    项目初始化:采用TypeScript 我们的版本是: $ node --version v8.5.0 $ npm --version 5.5.1 npm版本升级了,因为npm最近带来了新特性,本地会生 ...

  6. 随机四则运算的出题程序java

    一.设计思想 1.功能较多必须有菜单选择项,将一个大程序分为若干个功能模块的小程序,逐个实现2.针对题目避免重复时先将已生成的算式保存,然后将下一条生成的式子进行判断是否已生成,如果生成则返回循环语句 ...

  7. C语言第一周作业

    题目一:7-3 温度转换 本题要求编写程序,计算华氏温度150°F对应的摄氏温度.计算公式:C=5×(F−32)/9,式中:C表示摄氏温度,F表示华氏温度,输出数据要求为整型. 1.实验代码 2.设计 ...

  8. c语言博客作业-指针

    一.PTA实验作业 题目1: 1. 本题PTA提交列表 2. 设计思路(用代码表示扣分) 定义整型变量i,count记录平均分,实型变量sum保存总分 for i=0 to n sum = sum+* ...

  9. 《Language Implementation Patterns》之 解释器

    前面讲述了如何验证语句,这章讲述如何构建一个解释器来执行语句,解释器有两种,高级解释器直接执行语句源码或AST这样的中间结构,低级解释器执行执行字节码(更接近机器指令的形式). 高级解释器比较适合DS ...

  10. python的Virtualenv

    Virtualenv 虚拟的 Python 环境(简称 venv) 是一个能帮助你在本地目录安装不同版本的 Python 模块的 Python 环境,你可以不再需要在你系统中安装所有东西就能开发并测试 ...