[知识点]字符串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次的区间查询,输出每次查询区间中不同 ...
随机推荐
- Sublime text 3如何编辑less并转(编译)成css文件
今天开始学习使用less这个强大方便的前端工具,本来是考虑用koala(专门编辑less的软件)来使用less的,但是发现sublime编辑器也可以实现对less的编译及高亮显示代码,这样既能少用一个 ...
- android键盘输入读取
android键盘输入读取 监控android键盘输入方式有两种,一种在java层实现,重写onKeyDown和onKeyUp方法.另一种是在jni层实现,监控/dev/input/event0键盘 ...
- protocol http not supported or disabled in libcurl apt-get
ubuntu 14.04 碰到了这个莫名其妙的问题.谷歌了一把,解决方案如下:http://askubuntu.com/questions/683857/curl-1-protocol-https-n ...
- Ajax跨域:jsonp还是CORS
跨域一般用jsonp,兼容性比较好.CORS是html5最新的XHR第二版本,不支持IE8,IE9,对移动端的支持非常好.但是考虑项目后期这部分会转到同域名下,而且网址不需要支持ie8,ie9,所以我 ...
- 在CentOS 7.2下升级gcc编译器的版本
默认情况下,CentOS 7.2预装的gcc版本是4.8.x,通过执行命令 gcc -v 可以看到,一般情况下这个版本的编译器已经满足需要了,但是某些特殊的时候为了支持C++更高的特性,需要对gcc编 ...
- (三)Netty源码学习笔记之boss线程处理流程
尊重原创,转载注明出处,原文地址:http://www.cnblogs.com/cishengchongyan/p/6160194.html 本文我们将先从NioEventLoop开始来学习服务端的 ...
- maven学习(上)- 基本入门用法
一.下载及安装 1.1 下载maven 3.1.1 先到官网http://maven.apache.org/download.cgi 下载最新版本(目前是3.1.1 ),下载完成后,解压到某个目录(本 ...
- 重写jquery的ajax方法
//首先备份下jquery的ajax方法 var _ajax=$.ajax; //重写jquery的ajax方法 $.ajax=function(opt){ //备份opt中error和success ...
- STM32F412应用开发笔记之六:使用片上Flash存储参数
我们的项目中需要保存一些系统配置参数,这些数据的特点是:数量少而且不需要经常修改,但又不能定义为常量,因为每台设备可能不一样而且在以后还有修改的可能.这就需要考虑这些参数保存的问题.将这类数据存在指定 ...
- ASP.NET获取百度地图提供的API接口里面的JSON
思路:开始是想直接在前台获取,但是跨域访问还是有点难度,而且格式必须是josnp格式的,最后嫌麻烦,不得已放弃. 我做的ASP.NET 而这个有自带的解析类,直接引用就行了 先在后台获取到JOSN: ...