哈希HASH的本质思想类似于映射、离散化。

哈希,通过给不同字符赋不同的值、并且钦定一个进制K和模数,从而实现一个字符串到一个模意义下的K进制数上。

它的主要目的是判重,用于$DFS$、$BFS$判重(八数码),字符串判断相等、出现等等。

本篇总结字符串哈希以及一些应用例题。

为什要用字符串哈希?

因为取出一个字符串是$O(n)$的,比较一遍又是$O(n)$的,况且要比较两个甚至多个。这就成了$n^2$级别的了。

那我们比较数字怎么就不用这么麻烦呢?因为数字可以直接比较,(虽然不知道内部是怎么实现的,反正比一位一位比较肯定快)所以我们考虑把字符串映射到数字上。

就有了字符串哈希。

通过字符串哈希,只要题目支持预处理,我们可以$O(n)$预处理之后,$O(1)$进行提取,$O(1)$进行判重。

字符串哈希需要什么?

1.字符。初始坐标无所谓。

2.K进制数,通常选择$131$,$13331$,这两个质数冲突几率很小(不要问我为什么)。

3.取模数,我用过 $1e9+7$,$998244353$,用$2^{64}$也可以,这里利用自然溢出,一般不会有问题。提一句,$unsigned\space long\space long$做减法,即使算出来应该是负数,会自动加上$2^{64}$,相当于$(a+mod-b)%mod$了。没有问题。

处理hash:

1.预处理$K^{len}$ 放入$k[]$中储存。

2.顺便处理$hash[i]=hash[i-1]*K+str[i]$

hash的容器:

1.一个题可能产生很多哈希值。有的时候我们要找一个容器存储。能够比较快速地查询一个$hash$值有没有出现过。

2.比较常用的是$map<ll,bool>$,因为本身map就是映射。

3.但是$map$不但有$logn$,常数也不小。于是就有了hash表。

其实就是对$hash$值再分类存放。就可以避免很多没有意义的查询。

再找一个模数,一般是所有哈希值出现次数的几分之一(数组能开下),可以的话,就取出现次数也行。

然后,哈希值先除以模数,余数就是位置。然后用邻接表存储。

字符串哈希的基本操作:

1.提取:$a[l,r]$段:$hash[r]-hash[l-1]*k[r-l]$ 类似前缀和。

2.插入,同处理。

操作均是$O(1)$

字符串哈希支持的应用操作:

1.判断字符串是否相等。取hash段比较即可,$O(1)$

2.找某两个位置开始的$LCP$(最长公共前缀),二分位置+$hash$判断 $O(logn)$ (长度够小,可用$trie$树,更好的支持多串$LCP$)(当然,如果你会$SA$,这些都是小儿科~)

3.判断两个串字典序大小,找$LCP$,判断下一位大小。$O(logn)$

4.找回文串。但是要正反二分。如果可以预处理的话,当然不如$manacher$。或者你用SA建反串然后找LCP。

哈希冲突

1.由于取模,所以有一定几率,两个不同的串,但是哈希值相同。

我们认为哈希值相同,串就相同了。所以,就会出现错误。

像1e9+7,unsigned long long 这些,都可以特殊构造卡掉。

见bzoj HASH KILLER系列。

2.解决方法:

①取大质数作为模数。$10^{15}$以上的模数更不容易被卡。

②双哈希

即处理两个哈希值。相同的字符串一定两个都相同,因为都是同样的构造方法。

如果哈希值不同,一定是不同的字符串。

这个时候,如果两个串的两个哈希值对应相等,我们就认为相等。否则不等。

这样子冲突的概率就很小了。$1e9+7$,$998244353$的双模数就基本卡不掉了。

字符串哈希例题:

T1:POJ2758

给定一个字符串,要求维护两种操作
在字符串中插入一个字符
询问某两个位置开始的LCP
插入操作<=200,字符串长度<=5w,查询操作<=2w

分析:有人用后缀数组??不会。Splay??不会。

操作小于等于200,直接暴力重构是正解!!

注意:

1.插入字符位置可能远大于len,要向len+1取min

2.询问位置是初始位置,重构的时候,可以暴力循环记录每一个初始位置现在已经变到了第几个位置。

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=+;
const int mod=;
const int K=;
ll h[N];
ll c[N];
int n,m;
int len;
int f[N];
int ne[N];
char o[N],a[N];
int main()
{
scanf("%s",o+);
n=strlen(o+);
memcpy(a+,o+,sizeof o);len=n;
//cout<<" lenn "<<len<<endl;
scanf("%d",&m);
for(int i=;i<=n;i++) ne[i]=i;
c[]=;
for(int i=;i<=n+m+;i++) {
c[i]=(c[i-]*K)%mod;
if(i<=n) h[i]=(h[i-]*c[]+(int)o[i])%mod;
}
char ch,op;
int num,x,y;
//cout<<"fir "<<a+1<<endl;
while(m--){
scanf(" %c",&op); if(op=='Q'){
scanf("%d%d",&x,&y);
x=ne[x],y=ne[y];
//cout<<x<<" and "<<y<<endl;
if(a[x]!=a[y]){
printf("0\n");continue;
}
int ans;
int l=,r=min(len-x,len-y)+;
//cout<<" origin "<<l<<" "<<r<<endl;
while(l<=r){
int mid=(l+r)>>;
int ed1=x+mid-;
int ed2=y+mid-;
ll ha1=(h[ed1]+mod-h[x-]*c[mid]%mod)%mod;
ll ha2=(h[ed2]+mod-h[y-]*c[mid]%mod)%mod;
//cout<<mid<<" hash "<<ha1<<" "<<ha2<<endl;
if(ha1==ha2) {
ans=mid,l=mid+;
}
else{
r=mid-;
}
}
printf("%d\n",ans);
}
else{
scanf(" %c%d",&ch,&num);
if(num>len) num=len+;
///add(num);
len++;
for(int i=len;i>=num+;i--) a[i]=a[i-];
a[num]=ch;
for(int i=num;i<=len;i++) h[i]=(h[i-]*c[]+(int)a[i])%mod;
for(int i=n;i>=;i--) {
if(ne[i]>=num) ne[i]++;else break;}
}
//cout<<a+1<<endl;
}
return ;
}

POJ2758

以下是配赠福利

树哈希:

我们知道,一棵无根树可以以任何一个点为根。两个树可能看过去形态不同,但是可能存在固定两个树的根,然后对整个树重新编号,使得完全相同。

求树的同构就是这样。

类似字符串同构,我们也要适用哈希。

模板例题:

BZOJ 4337: BJOI2015 树的同构

50棵树,50个节点。求同构。

方法:
1.对于两个同构的树,存在固定两个树的根,然后对整个树重新编号,使得完全相同。

所以,我们可以对一个树,以每个点为根,然后dfs一遍。

$dfs$的时候,处理子树的$hash$值。

$hash$的$base$值和第几个儿子有关。是各不相同的素数。

然后,对于一个子树,把所有的儿子$hash$值,排序,从小到大合并。

然后对于所有的$hash$值,$sort$一遍。

两个树相同,当且仅当所有的$N$个点的$hash$值对应相同。

我们的$hash$值考虑了深度、每个点节点个数。所以不容易冲突。

2.我们之所以要以每个点为根,然后$dfs$一遍,

是因为可能重新编号后根不知道是哪两个。

但是这样比较暴力。$N^3$

发现,对于一个无根树,重心最多两个。

对于两个同构的树,如果我们把重心的搭配4种枚举一下,那么必然存在一种树的$hash$相同。

所以,可以对每个树以重心扫两边即可。

$hash$的$base$,也可以考虑用欧拉序。

HASH 字符串哈希 映射转化的更多相关文章

  1. 数据结构作业——hash(字符串哈希)

    Hash Description 给定长度为 n ( n<=1000000)的字符串,字符串仅由小写字母的前 m ( m<=6) 个字符组成,请你计算出共有多少长度为 k( k<=6 ...

  2. 详解HASH(字符串哈希)

    HASH意为(散列),是OI的常用算法. 我们常用哈希的原因是,hash可以快速(一般来说是O(段长))的求出一个子段的hash值,然后就可以快速的判断两个串是否相同. 今天先讲string类的has ...

  3. 从Hash Killer I、II、III论字符串哈希

    首先,Hash Killer I.II.III是BZOJ上面三道很经典的字符串哈希破解题.当时关于II,本人还琢磨了好久,但一直不明白为啥别人AC的代码都才0.3kb左右,直到CYG神犇说可以直接随机 ...

  4. Redis支持的数据类型及相应操作命令:String(字符串),Hash(哈希),List(列表),Set(集合)及zset(sorted set:有序集合)

    help 命令,3种形式: help 命令 形式 help @<group> 比如:help @generic.help @string.help @hash.help @list.hel ...

  5. 字符串哈希hash

    题目描述 如题,给定N个字符串(第i个字符串长度为Mi,字符串内包含数字.大小写字母,大小写敏感),请求出N个字符串中共有多少个不同的字符串. 友情提醒:如果真的想好好练习哈希的话,请自觉,否则请右转 ...

  6. 【基本算法入门-字符串哈希(Hash)】-C++

    字符串哈希入门 说得通俗一点,字符串哈希实质上就是把每个不同的字符串转成不同的整数. 为什么会有这样的需要呢?很明显,存储一个超长的字符串和存储一个超大但是能存的下的整数,后者所占的空间会少的多,但主 ...

  7. Crazy Search POJ - 1200 (字符串哈希hash)

    Many people like to solve hard puzzles some of which may lead them to madness. One such puzzle could ...

  8. 牛客练习赛33 E tokitsukaze and Similar String (字符串哈希hash)

    链接:https://ac.nowcoder.com/acm/contest/308/E 来源:牛客网 tokitsukaze and Similar String 时间限制:C/C++ 2秒,其他语 ...

  9. luoguP3370 【模板】字符串哈希 [hash]

    题目描述 如题,给定N个字符串(第i个字符串长度为Mi,字符串内包含数字.大小写字母,大小写敏感),请求出N个字符串中共有多少个不同的字符串. 友情提醒:如果真的想好好练习哈希的话,请自觉,否则请右转 ...

随机推荐

  1. 以太坊remix-ide本地环境搭建

    remix-ide简介 ​ remix-ide是一款以太坊官方solisity语言的在线IDE,可用于智能合约的编写.测试与部署,不过某些时候可能是在离线环境下工作或者受限于网速原因,使用在线remi ...

  2. Docker环境编译时的错误记录

    1)报错一docker-compose -f compose/app.yaml -f compose/backend.yaml -f compose/proxy.yaml build peatio b ...

  3. Mysql优化系列(1)--Innodb重要参数优化

    1.简单介绍InnoDB给MySQL提供了具有提交,回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎.InnoDB锁定在行级并且也在SELECT语句提供一个Oracle风格一致的非锁定读.这些特色 ...

  4. vs感受,由于我的电脑装了俩年了!我直接写感受吧

    个人感受:最初的感觉,最开始装vs是因为我的电脑8.1不兼容vc6.0(一个挺坑的编程软件),最开始用vs的时候我还是一个小白什么都不懂,vs创建项目实在是太复杂,不看教程根本看不懂,也许是它能包容的 ...

  5. C#【结对编程作业】小学数学习题助手

    一.软件成品展示 软件本体下载(包括程序及其更新日志,源码工程包,UML图,API接口文档,算法介绍文档,算式计算excel实例,浅查重程序) 链接: http://pan.baidu.com/s/1 ...

  6. 生命游戏&一维细胞自动机 笔记

    de 生命游戏是一种简单的聚合模型,展示了事物是如何聚合的,是自动机(CA)模型的一种.由剑桥大学约翰康威发明,其规则为: 1. 每个细胞拥有八个邻居,细胞状态只有存活(黑)和死亡(白)两种: 2.处 ...

  7. 阅读<构建之法>10、11、12章

    第十章: 典型用户和场景对后面工作有什么帮助吗? 第十一章: 每日构建的目的是什么呢?有没有具体说明? 第十二章: 产品定位人群是否也局限了产品的可拓展性?

  8. 第三次Sprint-最后冲刺

    由于一些原因,导致我和汝婷被退队了.因此我们是从上星期重新开始做系统. 陈汝婷单独负责: 1.用户输入题目数: 2.限制题数: 3.自动生成用户需要题目数的题目: 4.计时 练丽云单独: 1.异常处理 ...

  9. 【Deep Hash】NINH

    [CVPR 2015] Simultaneous Feature Learning and Hash Coding with Deep Neural Networks [paper] Hanjiang ...

  10. java 值传递 数组传递

    在java中,不允许程序员选择值传递还是地址传递各个参数,基本类型总是按值传递.对于对象来说,是将对象的引用也就是副本传递给了方法,在方法中只有对对象进行修改才能影响该对象的值,操作对象的引用时是无法 ...