如果你已经确保自己的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. poj1200-Crazy Search(hash入门经典)

    Hash:一般是一个整数.就是说通过某种算法,可以把一个字符串"压缩" 成一个整数.一,题意: 给出两个数n,nc,并给出一个由nc种字符组成的字符串.求这个字符串中长度为n的不同 ...

  2. COGS 902 乐曲主题 题解 & hash入门贺

    [题意] 给定一个长为n的序列,元素都是不超过88的正整数,求序列中主题的最大长度. 所谓主题是指在序列中出现了至少两次并且不相交的子串.特别的,主题可以变调,也就是说如果一个子串全部加上或减去一个数 ...

  3. HDU 1880 字符串hash 入门题

    Problem Description 哈利波特在魔法学校的必修课之一就是学习魔咒.据说魔法世界有100000种不同的魔咒,哈利很难全部记住,但是为了对抗强敌,他必须在危急时刻能够调用任何一个需要的魔 ...

  4. 字符串hash入门

    简单介绍一下字符串hash 相信大家对于hash都不陌生 翻译过来就是搞砸,乱搞的意思嘛 hash算法广泛应用于计算机的各类领域,像什么md5,文件效验,磁力链接 等等都会用到hash算法 在信息学奥 ...

  5. POJ 1200 Crazy Search【Hash入门】

    RK法:https://www.cnblogs.com/16crow/p/6879988.html #include<cstdio> #include<string> #inc ...

  6. bzoj 2803 [Poi2012]Prefixuffix 兼字符串hash入门

    打cf的时候遇到的问题,clairs告诉我这是POI2012 的原题..原谅我菜没写过..于是拐过来写这道题并且学了下string hash.   字符串hash基于Rabin-Karp算法,并且对于 ...

  7. Oulipo(Hash入门第一题 Hash函数学习)

    Hash,一般翻译做散列.杂凑,或音译为哈希,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值.这种转换是一种压缩映射,也就是,散列值的 ...

  8. 简单 hash 入门题目

    题目描述 NOIP 复赛之前,HSD 桑进行了一项研究,发现人某条染色体上的一段 DNA 序列中连续的 kkk 个碱基组成的碱基序列与做题的 AC 率有关!于是他想研究一下这种关系.现在给出一段 DN ...

  9. hash进阶:使用字符串hash乱搞的姿势

    前言 此文主要介绍hash的各种乱搞方法,hash入门请参照我之前这篇文章 不好意思hash真的可以为所欲为 在开头先放一下题表(其实就是我题解中的hash题目qwq) 查询子串hash值 必备的入门 ...

随机推荐

  1. 深入了解oracle存储过程的优缺点

    定义: 存储过程(Stored Procedure )是一组为了完成特定功能的SQL 语句集,经编译后存储在数据库中.用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它.存储过程是 ...

  2. PHP导出excel时数字变为科学计数的解决方法

    在数据导出到excel时数字格式不对,一般分为以下两种情况. 1.excel单元格设置长度不够 解决方法: //在excel.php文件中 $objActSheet = $objPHPExcel-&g ...

  3. Scala系统学习(四):Scala数据类型

    Scala与Java具有相同的数据类型,具有相同的内存占用和精度.以下是提供Scala中可用的所有数据类型的详细信息的表格: 序号 数据类型 说明 1 Byte 8位有符号值,范围从-128至127 ...

  4. [vue]vue-book

    我们打算要做这几个模块 首页 列表 收藏 添加 home.vue --> list.vue -->app.vue --> main.js 安装环境 npm i vue-cli -g ...

  5. POJ3096:Surprising Strings(map)

    http://poj.org/problem?id=3096 for循环真是奇妙! #include <string.h> #include <stdio.h> #includ ...

  6. vs2010用NuGet(程序包管理)安装EF失败之解决办法

    今天用程序包管理控制台安装EF.报错.如下

  7. Vue.Js添加自定义插件

    基于上篇我们讲了 在window下搭建Vue.Js开发环境 我们可以开发自己的vue.js插件发布到npm上,供大家下载使用. 1.首先打开cmd命令窗口,进入我们的工作目录下 执行 cd E:\vu ...

  8. CUDA从入门到精通 - Augusdi的专栏 - 博客频道 - CSDN.NET

    http://blog.csdn.net/augusdi/article/details/12833235 CUDA从入门到精通 - Augusdi的专栏 - 博客频道 - CSDN.NET CUDA ...

  9. centos迷你版,没有安装ifconfig命令

    ifconfig命令是设置或显示网络接口的程序,可以显示出我们机器的网卡信息,可是有些时候最小化安装CentOS等Linux发行版的时候会默认不安装ifconfig等命令,这时候你进入终端,运行ifc ...

  10. PKU2418_树种统计(map应用||Trie树)

    Description Hardwoods are the botanical group of trees that have broad leaves, produce a fruit or nu ...