[知识点]字符串Hash
1、前言
字符串的几大主要算法都多少提及过,现在来讲讲一个称不上什么算法, 但是非常常用的东西——字符串Hash。
2、Hash的概念
Hash更详细的概念不多说了,它的作用在于能够对复杂的状态进行简单的表达,更方便的用于判重。在搜索的时候,或是动规的时候,都有过类似的做法。在实际应用中,也是非常重要的,这也就是为什么存在什么暴雪公司的Hash算法等等;加密环节也是Hash的重要之处,MD5码就是一个经典的例子。
字符串Hash的方式多式多样,重点来解释一下最简单的,最常用的。
3、BKDRHash
作为竞赛选手,BKDRHash必定是首选的!通过多种测试与研究,BKDRHash在简单Hash中是冲突量最小的一种Hash方式,处理起来也非常好理解。先看一段简短的代码:
------------------------------------------------------------------------------------------------------
#define x 131
#define MOD 1000000007
int h[MAXN];
char a[MAXN];
void getHash()
{
int len = strlen(a);
for (int i = 0; i <= len - 1; i++) h[i] = (h[i - 1] * x + a[i]) % MOD;
}
------------------------------------------------------------------------------------------------------
这种方式类似于进制数表示。对于一个字符串a的某一位i,乘上一个x^i,在x>=字符串中的字符数的情况下,Hash值和字符串必定是一一映射的,当且仅当在一一对应的情况下,我们的Hash值才有存在的含义。
但是显然的,有一个很严重的问题,倘若某个字符串规定只有10个字符,长度最长为8,则极限情况下Hash值为10^8。放入一个Hash值数组中,当然是可行的。但是在很多时候,字符串的长度和字符区域不会这么小的。这也是我最开始很困惑的地方,似乎无解的样子。这个时候,我们只能选择牺牲完美的正确性来满足条件了。
对于超过某个限度的数,我们给他取一个MOD。这样好像很逗的样子?我们简单分析一下出现重复的情况,在随机造数据求Hash值取模数1e9+7之后,冲突率为0.05%上下。在算法竞赛中,这种错误率几乎可以不计。
这也就有人问了,在非100%正确率的情况下,如果出题人丧心病狂的话,这不同样会出现用来卡你的数据?要知道,字符串Hash的灵活性是非常强的!上面代码中我们可以看出,x和MOD都是我们自己定义的,它并不取决于题目的数据(当然x是必须大于字符集的这个不用多说)。
上述代码中,我们的x设定值为131,我并不清楚为什么要用这个数,确实网上的很多做法和教程也都是131,但是其实局限性并没有这么强。只要这个数是大于等于出现的字符个数的,是都可以满足的,例如某提中提到全部为小写字母,x>=26即可。这个应该是不存在卡数据的。
关键就在于MOD的讲究。其实通过网上的,学长的各种表述,取MOD的方式也是各种花样。数值越大越好这个当然是必然的!但是在这个基础上取质数也是最好的。为什么说这个事有讲究的,可能在NOIP和一些比较基础的比赛/考试中,这个不影响;但是大考试中,如HNOI/CTSC中据说都出现过的专门卡特定MOD数的情况。比如1e9+7,1e6+7,1e9+1都是比较常见的,有些出题人故意尝试着去卡,导致某些选手分数降低。这个时候就可以花式取模了,只要是个较大的数,最好是个质数,比如你的生日之类的,如19990522之类的,谁会知道呢。
在平均情况下,采用伪随机数据,MOD<=5e6才会出现一个错误点,所以自己好好把握吧。
3、多Hash取值
我相信和我一样,许多人还是有后顾之忧,所以又出现了若干的方法使这个冲突率变得更小。因为对于算法竞赛,这个0.05%似乎不值得一提;但是对于大型工程,如软件,系统,游戏等,面对全球大量客户,不不难出现一些BUG。暴雪公司(Bilzzard)就有着自己的高效而巧妙的Hash值取法,这里不提;他们似乎对这个依旧不放心,于是还决定采取多Hash值的判断。
对于一个字符串,我们假定给他两个Hash值(BKDRHash和其他的类型,参见网上若干教程),当且仅当两个字符串的两个Hash值都是一样的,我们才认为这两个字符串相等。根据乘法原理,这已经显而易见了,冲突率就会是两个Hash方法的乘积之和,可见其量之小。
同样地,你也可以选择3个Hash,4个Hash,但是随着个数的增加,复杂度也会升高,所以一般取2~3个是足够了的。暴雪公司的设定冲突率为1:18889465931478580854784,大概是10^(-22.3)%,对一个游戏程序来说足够安全了。
4、Hash挂链
上述的多Hash取值相比挂链是要好理解一些的,暴雪公司就从来不挂链,但是作为一种方法,这里依旧要提及。Hash挂链才是真正的能够保证完美正确性的方法。同样先上一段代码。
------------------------------------------------------------------------------------------------------
vector <int> h[MAXN];
void add(int o)
{
int t = o % MOD;
for (int i = 1; i <= h[t].size(); i++)
if (h[t][i] == o) return;
h[t].push_back(o);
}
------------------------------------------------------------------------------------------------------
存放Hash值的容器为vector以节省空间,它的作用和链表一样(你也可以选择手写链表)。对于某一个数经过取模之后得到的数为t,它对应的vector为h[t]。我们将所有取模之后得到t的原值放入o,这样,可以很容易地发现是否冲突,只需要在vector中扫描一遍。
确实这个正确性是必定保证了的,但是相比之下,时间复杂度和空间复杂度似乎都不够理想,当然在能确保的情况下绝对是可以用的。
5、总结
字符串Hash,作用是无限的。KMP,SA等等,经常出现可以用字符串Hash代替的情况。
[知识点]字符串Hash的更多相关文章
- 【二分答案+智障的字符串hash】BZOJ2946-[Poi2000]公共串(Ranklist倒一达成!!!!!)【含hash知识点】
[题目大意] 给出几个由小写字母构成的单词,求它们最长的公共子串的长度. [字符串hash的小笔记] hash[i]=(hash[i-1]*p+idx(s[i]))%mod,idx为映射值,一般a.. ...
- 【BZOJ-3555】企鹅QQ 字符串Hash
3555: [Ctsc2014]企鹅QQ Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1545 Solved: 593[Submit][Statu ...
- POJ 1200 字符串HASH
题目链接:http://poj.org/problem?id=1200 题意:给定一个字符串,字符串只有NC个不同的字符,问这个字符串所有长度为N的子串有多少个不相同. 思路:字符串HASH,因为只有 ...
- LA4671 K-neighbor substrings(FFT + 字符串Hash)
题目 Source http://acm.hust.edu.cn/vjudge/problem/19225 Description The Hamming distance between two s ...
- 各种字符串Hash函数比较(转)
常用的字符串Hash函数还有ELFHash,APHash等等,都是十分简单有效的方法.这些函数使用位运算使得每一个字符都对最后的函数值产生影响.另外还有以MD5和SHA1为代表的杂凑函数,这些函数几乎 ...
- 字符串hash + 二分答案 - 求最长公共子串 --- poj 2774
Long Long Message Problem's Link:http://poj.org/problem?id=2774 Mean: 求两个字符串的最长公共子串的长度. analyse: 前面在 ...
- 字符串hash - POJ 3461 Oulipo
Oulipo Problem's Link ---------------------------------------------------------------------------- M ...
- 长度有限制的字符串hash函数
长度有限制的字符串hash函数 DJBHash是一种非常流行的算法,俗称"Times33"算法.Times33的算法很简单,就是不断的乘33,原型如下 hash(i) = hash ...
- hdu 4622 Reincarnation 字符串hash 模板题
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4622 题意:给定一个长度不超过2000的字符串,之后有不超过1e5次的区间查询,输出每次查询区间中不同 ...
随机推荐
- 二刷Cracking the Coding Interview(CC150第五版)
第18章---高度难题 1,-------另类加法.实现加法. 另类加法 参与人数:327时间限制:3秒空间限制:32768K 算法知识视频讲解 题目描述 请编写一个函数,将两个数字相加.不得使用+或 ...
- DNS解析过程详解
先说一下DNS的几个基本概念: 一. 根域 就是所谓的“.”,其实我们的网址www.baidu.com在配置当中应该是www.baidu.com.(最后有一点),一般我们在浏览器里输入时会省略后面的点 ...
- Moving Average from Data Stream
Given a stream of integers and a window size, calculate the moving average of all integers in the sl ...
- Redis 3.0 Cluster集群配置
Redis 3.0 Cluster集群配置 安装环境依赖 安装gcc:yum install gcc 安装zlib:yum install zib 安装ruby:yum install ruby 安装 ...
- Mongodb 基础(Z)
Mongodb的客户端支持 作为一款非常成熟NoSQL数据库,Mongdb对各种编程语言的支持已经非常完善了,目前已经支持各大主流编程语言包括:1,mongo shell 2,Python 3,Jav ...
- 实现放大转场动画 from cocoachina
原文1:http://www.cocoachina.com/ios/20160318/15714.html 原文2:http://ningandjiao.iteye.com/blog/2049105 ...
- Atom安装以及activate-power-mode atom package插件安装
1.首先安装node.js,不然没有npm命令可用. 现在很多开源程序都使用npm来管理依赖包,所以node.js必备呀. 2.去下载atom安装包和activate-power-mode-0.5.2 ...
- [不断更新]iOS开发常用技术
1.修改默认初始化方法 构建便利构造器 修改默认init初始化 .m文件中 @implementation 类名 -(id)init{ self=[super init]; printf(" ...
- 在Ubuntu 14.04安装和使用Docker
Docker是一个开源软件,它可以把一个Linux应用和它所依赖的一切(比如配置文件)都封装到一个容器.然而,Docker与虚拟机不同,它使用了沙箱机制,Docker容器不运行操作系统,它共享主机上的 ...
- Java注解基础概念总结
注解的概念 注解(Annotation),也叫元数据(Metadata),是Java5的新特性,JDK5引入了Metadata很容易的就能够调用Annotations.注解与类.接口.枚举在同一个层次 ...