什么是字符串哈希
其实字符串hash和我们平时在代码中使用的hash都是一样的,原理都是将字符串通过一系列算法,将他映射成一个N进制,并且我们要保证不同的字符串得到的N进制一定是不同的。
为什么要有字符串hash这个东西?试想一下,我们对大量的单词进行对比比较,看哪些单词是重复出现过的,我们首先要将单词转换成ascii码,然后再比较他们的ascii码是否相等,这样会不会太浪费空间和时间,我们不妨先计算出这个单词的hash,然后再保存在内存里,这样我们比较两个单词,直接比较他们的字符串hash即可。至于如何计算字符串hash,其实就跟我们计算二进制,八进制一样,只是有一点细节上的区别。
如何计算
假设我们有abc
和abb
两个字符串,假设1=a,2=b,3=c…,英文字母有26个,我们其实可以设置26进制即可,但这样容易出现hash碰撞,所以我们设置大一点,随便一个数字就好,就36进制吧。
通过进制计算,我们得出:
hash(abc) = 1371
hash(abb) = 1370
由于在计算较长的字符串时,我们知道int
最大可以保存2^31-1
,如果字符串过长的话,肯定会出现溢出的情况,这个时候我们就要对结果取模,一般这儿设置一个较大的质数用来取模即可,我这里用的是1e+9 + 7
这个数来进行取模,同时这个数刚好在int
范围内。
所以我们可以得出一个计算字符串hash的公式。
其中 h 为返回值,初始为 0,p为所代表的进制,Si代表当前字符串第i个字符。
衡量hash的一个指标就是看hash碰撞率,如果某字符串越长越复杂,那计算出来的hash值进行取模后,可能会与其他字符串的hash值发生碰撞。
这个问题是不可避免的,你可以增大基数p或者增大模来降低hash碰撞,也可以采用其他的hash策略来计算hash。
总而言之,在平时刷题中,题目是不会出这种数据给你的,如果有,改变一下模的值就行了。
代码
我的cpp代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
unsigned int MOD = 1e+9 + 7;
int getH(string s) {
unsigned int h = 0;
int n = s.size();
int p = 36; // 36进制
for (int i = 0; i < n; ++i) {
int c = s[i] - 'a' + 1;
h = (h * p) % MOD + c;
}
return h;
}
int main() {
cout << getH("abb") << endl;
cout << getH("abc");
return 0;
}