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的更多相关文章

  1. 【二分答案+智障的字符串hash】BZOJ2946-[Poi2000]公共串(Ranklist倒一达成!!!!!)【含hash知识点】

    [题目大意] 给出几个由小写字母构成的单词,求它们最长的公共子串的长度. [字符串hash的小笔记] hash[i]=(hash[i-1]*p+idx(s[i]))%mod,idx为映射值,一般a.. ...

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

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

  3. POJ 1200 字符串HASH

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

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

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

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

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

  6. 字符串hash + 二分答案 - 求最长公共子串 --- poj 2774

    Long Long Message Problem's Link:http://poj.org/problem?id=2774 Mean: 求两个字符串的最长公共子串的长度. analyse: 前面在 ...

  7. 字符串hash - POJ 3461 Oulipo

    Oulipo Problem's Link ---------------------------------------------------------------------------- M ...

  8. 长度有限制的字符串hash函数

    长度有限制的字符串hash函数 DJBHash是一种非常流行的算法,俗称"Times33"算法.Times33的算法很简单,就是不断的乘33,原型如下 hash(i) = hash ...

  9. hdu 4622 Reincarnation 字符串hash 模板题

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4622 题意:给定一个长度不超过2000的字符串,之后有不超过1e5次的区间查询,输出每次查询区间中不同 ...

随机推荐

  1. 获取pe文件的文件类型

    工程文件petype.cpp通过调用pefile类中的函数获取文件类型. 文件类型的判断通过5个监测点完成. 监测点1:dos头的e_magic 监测点2:nt头的Signature 监测点3:文件头 ...

  2. springmvc.xml的基本配置

    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w ...

  3. BZOJ4591——[Shoi2015]超能粒子炮·改

    1.题意:求 2.分析:公式恐惧症的同学不要跑啊QAQ 根据lucas定理-- 这一步大家都能懂吧,这是浅而易见的lucas定理转化过程,将每一项拆分成两项 那么下一步,我们将同类项合并 我们观察可以 ...

  4. Git使用- 基本命令

    $ git config --global user.name "Your Name"   全局 name 设置 $ git config --global user.email ...

  5. CentOS 7 Git安装

    Git安装 yum -y install git 安装后,在srv目录下建立Git的目录. 初始化一个git空仓库 git init --bare project.git 增加用于访问git仓库的用户 ...

  6. bootstrap validate 实现页面动态验证(formvalidate)

    关于基本的bootstrap validate 验证方法外面有许多博客上都有讲解,我就不在过多叙述了.大家也可以去看官网api:http://bv.doc.javake.cn/api/ 今天要说的是动 ...

  7. linux回退到上次访问目录

    cd / cd ..  回到上级目录 cd - 回到上次访问目录

  8. javascrit原生实现jquery的append()函数

    /** * javascrit原生实现jquery的append()函数 * @param parent * @param text */ function append(parent, text) ...

  9. firefox插件HTTP-Tool的使用方法

    2016年11月3日 14:32:01 星期四 chrome 有postman很强大 我比较懒, 不想FQ, 经常用firefox, 试了几款模拟post请求的插件, 觉得http-tool挺简洁的 ...

  10. 【转载】SSM框架整合

    http://blog.csdn.net/gebitan505/article/details/44455235/ 1.基本概念 1.1.Spring Spring是一个开源框架,Spring是于20 ...