简单介绍一下字符串hash

相信大家对于hash都不陌生

翻译过来就是搞砸,乱搞的意思嘛

hash算法广泛应用于计算机的各类领域,像什么md5,文件效验,磁力链接 等等都会用到hash算法

在信息学奥赛中,hash算法主要应用于搜索状态判重,字符串的比较等

hash的主要思想是:对于一个空间、时间需求较大的状态,在一定错误率的基础上进行状态压缩,降低其时间、空间的需求量

对于字符串hash来说,就是把一串字符串压缩成一个hash值,方便我们进行数据的处理

接下来我们重点讲一下字符串hash的实现方法

实现方法

思想

在信息学奥赛中,使用最广泛的算法叫做:BKDR Hash

它的核心思想是:

对于一个字符串,选取恰当的进制,将一个字符串看做是一个大整数

(众人:***,你这是要让我们写高精啊)

然后再对一个随便什么数取模就可以啦

当然这个“恰当的进制”和“随便什么数”是有讲究的

根据砖家的研究:

进制一般选择大于字符串中的最大的字符且不含模数的值因子的数

比如说,如果你是对一串小写字母做字符串hash,那么131这个进制就是不错的选择

而“随便什么数”有三种方法

  1. 选择两个模数,判断的时候只有两个hash值都相同才算相同
  2. 选择一个大质数,像11111111111111111111111或者212370440130137957
  3. 用unsigned long long 自然溢出

注意:尽量不要只用一个较小的质数,根据生日悖论,很容易被卡

代码

代码实现比较简单

https://www.luogu.org/problemnew/show/3370

  • unsigned long long 自然溢出
 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<map>
#define lli long long int
using namespace std;
const int MAXN=;
int seed=;
void read(int &n)
{
char c='+';int x=;bool flag=;
while(c<''||c>''){c=getchar();if(c=='-')flag=;}
while(c>=''&&c<='')x=x*+c-,c=getchar();
n=flag==?-x:x;
}
char a[MAXN];
map<long long ,bool>happen;
int tot=;
int main()
{
int n;
read(n);
for(int i=;i<=n;i++)
{
unsigned long long base=;
scanf("%s",a);
for(int j=;j<strlen(a);j++)
base=base*seed+(a[j]);
if(!happen[base])
happen[base]=,tot++;
}
printf("%d",tot);
return ;
}

ull

  • 双hash
 #include<cstdio>
#include<cstring>
#include<algorithm>
#define ull unsigned long long
using namespace std;
const int MAXN=2e7+;
const int mod1=;
const int mod2=;
const int seed=;
const int seed2=;
int n;
struct node
{
char s[];
int h1,h2,l;
}a[];
ull gethash1(char *s,int l)
{
ull ans=;
for(int i=;i<=l;i++)
ans=(ans*seed+(ull)s[i])%mod1;
return ans; }
ull gethash2(char *s,int l)
{
ull ans=;
for(int i=;i<=l;i++)
ans=(ans*seed2+(ull)s[i])%mod2;
return ans;
}
int hash1[MAXN],hash2[MAXN];
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)
{
scanf("%s",a[i].s+);
a[i].l=strlen(a[i].s+);
}
for(int i=;i<=n;i++)
{
a[i].h1=gethash1(a[i].s,a[i].l)%mod1;
a[i].h2=gethash2(a[i].s,a[i].l)%mod2;
}
int ans=;
for(int i=;i<=n;i++)
if(hash1[a[i].h1]==||hash2[a[i].h2]==)
hash1[a[i].h1]=,hash2[a[i].h2]=,ans++;
printf("%d",ans);
return ;
}

双hash

  • 大质数hash
 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<map>
#define lli long long int
using namespace std;
const int MAXN=;
const long long int mod=;
int seed=;
void read(int &n)
{
char c='+';int x=;bool flag=;
while(c<''||c>''){c=getchar();if(c=='-')flag=;}
while(c>=''&&c<='')x=x*+c-,c=getchar();
n=flag==?-x:x;
}
char a[MAXN];
map<int,bool>happen;
int tot=;
int main()
{
int n;
read(n);
for(int i=;i<=n;i++)
{
long long base=;
scanf("%s",a);
int la=strlen(a);
for(int j=;j<la;j++)
base=(base*seed+(a[j]) )%mod;
if(!happen[base])
happen[base]=,tot++;
}
printf("%d",tot);
return ;
}

大质数

特别注意:在循环的时候,不要写: for(int j=;j<strlen(a);j++) !!!!因为strlen的复杂度是$O(n^2)$,数据稍微一大你就会被卡T

正确的姿势是把长度记录下来 int la=strlen(a); for(int j=;j<la;j++)

常用技巧:区间hash值

在进行字符串hash的时候,我们经常会用到某一段的hash值

这时候怎么办呢?

假设我们已经得到了$hash[1-r]$

那么$hash[l-r]=hash[r]-seed^{r-l+1}*hash[l - 1]$(seed表示进制)

举个例子

$hash[1]=a1$

$hash[2]=a1*base+a_2$

$hash[3]=(a_1*seed+a_2)*seed+a_3=a_1*seed^2+a_2*seed+a_3$

$hash[4]=[(a[1*seed+a_2)*seed+a_3]*seed+a_4=a1*seed^3+a2*seed^2+a_3*seed+a_4$

$hash[3-4]=hash[4]-base^{2}*hash[2]=a_3*seed+a_4$

很明显可以看出这个公式是对的

最好自己手写一下,这样才能体会到其中的奥妙

经典题目

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

必做练手题,代码已经在上面给出了

洛谷 P3375 【模板】KMP字符串匹配

这道题可以用字符串hash水过

http://www.cnblogs.com/zwfymqz/p/7793347.html

洛谷P2264 情书

这道题理论上是可以用字符串hash做的

但是我只水到90分,各位神犇可以尝试一下

http://www.cnblogs.com/zwfymqz/p/7355159.html

BZOJ 3555: [Ctsc2014]企鹅QQ

略有难度

http://www.cnblogs.com/zwfymqz/p/7349658.html

洛谷P3823 蚯蚓排队

其他经典应用

以下内容来自远航之曲大神,在此表示衷心的感谢

1、kmp

问题:给两个字符串S1,S2,求S2是否是S1的子串,并求S2在S1中出现的次数

把S2 Hash出来,在S1里找所有长度为$|S2|$的子串,Hash比较。效率$O(|S1|)$

2、AC自动机

问题:给N个单词串,和一个文章串,求每个单词串是否是文章串的子串,并求每个单词在文章中出现的次数。

把每一个单词hash成整数,再把文章的每一个子串hash成整数,接下来只需要进行整数上的查找即可。

复杂度:$O(|A|2+|S|)$

用AC自动机可以做到$O(|A|+|S|)$的复杂度,|S|是单词串总长,$|A|$是文章长度

3、后缀数组

问题:给两个字符串S1,S2,求它们的最长公共子串的长度。

将S1的每一个子串都hash成一个整数,将S2的每一个子串都hash成一个整数

两堆整数,相同的配对,并且找到所表示的字符串长度最大的即可。

复杂度:$O(|S1|2+|S2|2)$

用后缀数组可以优化到$O(|S|log|S|)$

4、马拉车

问题:给一个字符串S,求S的最长回文子串。

先求子串长度位奇数的,再求偶数的。枚举回文子串的中心位置,然后二分子串的长度,直到找到一个该位置的最长回文子串,不断维护长度最大值即可。

复杂度:$O(|S|log|S|)$

用manacher可以做到$O(|S|)$的复杂度

5、扩展kmp

问题:给一个字符串S,求S的每个后缀与S的最长公共前缀

枚举每一个后缀的起始位置,二分长度,求出每个后缀与S的最长公共前缀。

复杂度:$O(|S|log|S|)$

用extend-kmp可以做到$O(|S|)$的复杂度

总结

字符串hash是一个非常优秀的算法。

希望大家能够熟练的掌握&&运用

说不定它可以在你写不出正解的时候帮你得很多分呢?

字符串hash入门的更多相关文章

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

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

  2. HDU 1880 字符串hash 入门题

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

  3. (通俗易懂小白入门)字符串Hash+map判重——暴力且优雅

    字符串Hash 今天我们要讲解的是用于处理字符串匹配查重的一个算法,当我们处理一些问题如给出10000个字符串输出其中不同的个数,或者给一个长度100000的字符串,找出其中相同的字符串有多少个(这样 ...

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

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

  5. [知识点]字符串Hash

    1.前言 字符串的几大主要算法都多少提及过,现在来讲讲一个称不上什么算法, 但是非常常用的东西——字符串Hash. 2.Hash的概念 Hash更详细的概念不多说了,它的作用在于能够对复杂的状态进行简 ...

  6. 【BZOJ-3555】企鹅QQ 字符串Hash

    3555: [Ctsc2014]企鹅QQ Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1545  Solved: 593[Submit][Statu ...

  7. POJ 1200 字符串HASH

    题目链接:http://poj.org/problem?id=1200 题意:给定一个字符串,字符串只有NC个不同的字符,问这个字符串所有长度为N的子串有多少个不相同. 思路:字符串HASH,因为只有 ...

  8. LA4671 K-neighbor substrings(FFT + 字符串Hash)

    题目 Source http://acm.hust.edu.cn/vjudge/problem/19225 Description The Hamming distance between two s ...

  9. 各种字符串Hash函数比较(转)

    常用的字符串Hash函数还有ELFHash,APHash等等,都是十分简单有效的方法.这些函数使用位运算使得每一个字符都对最后的函数值产生影响.另外还有以MD5和SHA1为代表的杂凑函数,这些函数几乎 ...

随机推荐

  1. 与JMeter的第一次亲密接触

    postman和JMeters是外部接口测试的两个工具,通过界面化的方法,来实现操作http报文携带的请求字段.VK值.json.cookie.header值及文件.本篇主要介绍JMeter.   测 ...

  2. JAVA小白开发环境配置(编译器为Idea)

    JDK配置 1.首先到官网下载最新版JDK:Oracle官网下载 Accept License Agreement–>下载适合自己pc版本的jdk(此处以64位windows为例.x86是32位 ...

  3. 【RL-TCPnet网络教程】第4章 RL-TCPnet网络协议栈简介

    第4章        RL-TCPnet网络协议栈简介 本章节介绍RL-TCPnet网络协议栈,让大家对 RL-TCPnet有一个整体的了解,RL-TCPnet是一款小型网络协议栈,适用于 ARM 内 ...

  4. [Swift]LeetCode97. 交错字符串 | Interleaving String

    Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. Example 1: Input: s1 = ...

  5. 本地安装MySQL详细教程

    第1章   MySQL的安装与使用 1.1  MySQL安装与配置 1.1.1 MYSQL的安装 1.打开下载的mysql安装文件mysql-5.5.27-win32.zip,双击解压缩,运行“set ...

  6. php 168任意代码执行漏洞之php的Complex (curly) syntax

    今天了解了php 168的任意代码执行漏洞,Poc: http://192.168.6.128/pentest/cms/php168/member/post.php?only=1&showHt ...

  7. PHP实现登录注册

    一.首先实现一个PHP的简单登录注册的话 我们要简单的与后端定义一下接口和传输数据的方式 并且我们要有一个phpStudy服务器. 第一步:当我们点击注册按钮的时候数据库要接收到客户端请求的数据  第 ...

  8. 甘果移动老甘:移动互联网变迁中的App和小程序

    2018 年 10 月13 日,由又拍云和知晓云联合主办的 Open Talk 丨2018 小程序开发者沙龙系列活动广州站拉开帷幕,甘果移动的 CEO 路文杰(老甘)在沙龙上做了<移动互联网变迁 ...

  9. 跳槽 & 思维导图

    个人博客原文: 跳槽 & 思维导图 今年的冬天有点"冷".给大家来点实在的东西. 不知道大家在跳槽的时候是怎么做的?直接投简历面试?还是准备了一段时间,复习一波知识点后再投 ...

  10. Redis学习——数据结构介绍(四)

    一.简介 作为一款key-value 的NoSQL数据库,Redis支持的数据结构比较丰富,有:String(字符串) .List(列表) .Set(集合) .Hash(哈希) .Zset(有序集合) ...