简介
通过Hash函数,将一个字符串映射成一个整数。这样我们就能很方便的使用Hash函数来判断两个字符串是否相等
思想
Hash函数的核心思想就是将输入值映射到一个值域较小,可以比较的范围,通常是整数。
这里的值域较小也不能太小,不然哈希冲突率会很高。 这里的值域较小在不同情况下意义不同。 对于哈希表,值域要小到线性的时空复杂度 而对于字符串哈希,只需要这个值域内的数字能快速比较就行。
哈希值(Hash函数)的特性
- 确定性:同样的字符串总是生成相同的哈希值。
- 高效性:计算速度快。
- 均匀性:不同的字符串尽量映射到不同的哈希值,减少冲突。
- 在 Hash 函数值不一样的时候,两个字符串一定不一样
- 在 Hash 函数值一样的时候,两个字符串不一定一样
计算
最常见的方式就是多项式哈希,假设字符串下标从1开始
- S[i] 通常是字符串中第 i 个字符的 ASCII 或 Unicode 值。
- P 是一个选择的常数,通常为31或37,131,13331等素数。
- M 是一个大素数,用于减少哈希冲突。
这种方法可以类比为一个P进制数来帮助理解。
Hash的分析与改进
减少哈希碰撞
为了减少哈希碰撞,最简单的方式就是使用合理且尽量大的素数M作为模数
在进行字符串哈希时,我们经常使用双哈希法,可以使用两个哈希函数,然后用加权的方式相加,或者直接比较两个哈希值的方式,这样的话哈希函数的值域就能扩大很多了且冲突率下降很多。
还可以用多哈希加权,哈希值字符串拼接,动态哈希选择等方式
多次询问子串哈希
单次计算一个字符串的哈希值是线性的。如果多次询问一个字符串的子串的哈希值,每次重新计算效率会十分低下。
一般采取的方法是对整个字符串先预处理出每个前缀的哈希值,将哈希值看成一个p进制的数对 M 取模的结果,这样的话每次就能快速求出子串的哈希了。
这样通过预处理,就能把截取子串哈希变成O()1了
应用
字符串匹配
求出模式串的哈希值,然后扫一遍文本串每个长度为模式串长度的字符串,判断一下哈希值是否相等即可
最长回文子串
二分答案最长回文子串长度,check时枚举回文子串的对称轴,哈希值判断两侧是否相等(这步需要分别预处理正着和倒着的哈希值)。时间复杂度为
也可以使用manacher算法在线性时间结束
最长公共子字符串
给定m个长度不大于n的字符串,求出所有字符串最长公共子字符串。 很显然这题可以二分答案最长公共子字符串长度,假设目前枚举的答案是k,那么check的逻辑就是将所有字符串长度为k的子串分别进行哈希,将哈希值放入n个哈希表中,然后判断是否存在交集即可。
时间复杂度为O(m+ nlogn)