哈希

简介

哈希是一种能把字符串(实际上数组也行,不过本文都会以字符串为例)映射成一个数的算法,哈希就是把一个字符串转成一个 \(K\) 进制数,但由于得到的数可能会非常大,所以其中会用到取模,因此哈希也有些玄学(建议 CF 有赛后 hack 的比赛不要使用哈希,或提高哈希的安全度)。

普通哈希

可以将 a b c ... z 分别映射为 \(1,2,3,\dots,26\) (本文默认只包含小写英文字母,如果有其它字符再进行映射即可)。

注意不能将 a 映射为 \(0\),因为这样哈希就会认为 ab \(=\) b

单次时间复杂度 \(O(|S|)\)

代码

const int base = 27, MOD = int(1e9) + 7;

int Hash(const string &s) {
  int res = 0;
  for(int i = 0; i < int(s.size()); ++i) {
    res = (1ll * res * base % MOD + s[i] - 'a' + 1) % MOD;
  }
  return res;
}

Hash(s);

前缀哈希

\(sum_i\) 表示前 \(i\) 个字符的哈希值,那么可以得到 \(sum_i=(sum_{i-1}\cdot base+mp_{s_i})\bmod m\),其中 \(base\) 指进制,\(mp_c\) 指字符 \(c\) 映射出的值,\(m\) 指模数。

我们还可以得到 \([l,r]\) 的哈希值是 \((sum_r - sum_{l-1}\cdot base^{r-l+1})\bmod m\)

预处理时间复杂度 \(O(|S|)\),单次时间复杂度 \(O(1)\)(因为可以预处理出 \(base^k\) 的值)。

代码

const int base = 27, MOD = int(1e9) + 7;

int Calc(int l, int r) {
  return ((sum[r] - 1ll * sum[l - 1] * Pow[r - l + 1] % MOD) % MOD + MOD) % MOD;
}

s = ' ' + s, Pow[0] = 1;
for(int i = 1; i <= n; ++i) {
  sum[i] = (1ll * sum[i - 1] * base % MOD + s[i] - 'a' + 1) % MOD;
  Pow[i] = 1ll * Pow[i - 1] * base % MOD;
}

Calc(l, r)

哈希的安全性

因为哈希使用了取模,所以就有可能不同的字符串会被当做是相同的。这种情况被称为哈希冲突

生日悖论

生日悖论是说:你有 \(30\) 个朋友,一年有 \(365\) 天(不考虑闰年),那么有朋友的生日在同一天的概率是多少?

可以先算出每个朋友都不相同的概率,然后用 \(1\) 减,可以得到:

\(1-\frac{365}{365}\cdot \frac{364}{365}\cdot\frac{363}{365}\cdot\dots\cdot\frac{336}{365}=1-\frac{365!}{335!\cdot365^{30}}\approx 70\%\)

这个故事告诉我们,哈希冲突的概率比你想得可能要大的多。

大质数

一种最简单的方法就是:让你的模数更大!(并且使用质数)

比如说你可以把模数设为:\(10^{18}+3,10^{18}+9,10^{18}+31\)(注意!\(10^{18}+7\) 不是质数!)。

或者把你的进制也改为质数,比如:\(131,133,13331,1145141\)

大质数表

代码

using ll = long long;

const ll base = 1145141, MOD = (ll)(1e18) + 3;

多重哈希

还有一种方法,就是多用几个模数(进制也可以用多个),只有在所有结果下都相同我们才认为两个字符串相同,这样也可以大大提升哈希的安全性。

建议在使用双重哈希时可以使用孪生素数(差为 \(2\) 的一对质数,如:\(10^9 + 7,10^9+9\))。

代码

using ll = long long;

const ll base[2] = {131, 1145141}, MOD[2] = {(ll)(1e18) + 3, (ll)(1e18) + 9};

int Hash(const string &s, int op) {
  int res = 0;
  for(int i = 0; i < int(s.size()); ++i) {
    res = ((__int128)res * base[op] % MOD[op] + s[i] - 'a' + 1) % MOD[op];
  }
  return res;
}

Hash(s, 0), Hash(s, 1);
posted @ 2024-04-16 17:21  Yaosicheng124  阅读(13)  评论(0编辑  收藏  举报