#1.字符串哈希
“十分简单易懂的基础字符串哈希教程”
字符串哈希
0x01. 什么是哈希
定义(摘自OI wiki)
我们定义一个把字符串映射到整数的函数
,这个 称为是 函数。
我们希望这个函数可以方便地帮我们判断两个字符串是否相等。
浅显地说:
把字符串以特定的方式表示为一串数,可以直接通过比较这一串数来判断字符串是否(相等/不同)。
0x02. 为什么要哈希
——复杂度优化
我们记字符串长度为
0x03. “特定的方式”
我们不妨用
一个十分粗糙与简陋的思路:
根据定义,想到可以将
hash += s[i] * Base
太弱了
Hack:对于任意的
实际上,以上这种情况我们称其为哈希冲突。
0x04. Hash 冲突
定义(摘自OI wiki)
冲突是指两个不同的字符串映射到相同的 值。
我们设的取值空间(所有可能出现的字符串的数量)为 ,计算次数(要计算的字符串数量)为 。
则冲突的概率为:
初学者不必深究此公式 证明CLICK HERE
0x05. 一种常用的 Hash
为了尽量确保字符串相同字符在不同位置的
多项式 Hash:
即
代码实现:
const int Base = 133 //*声明常量可以大大加快运算 int get_Hash(string S, int L) { int ans = 0; for (int i = 0; i < L; i++) { ans = ans * Base + S[i]; } return ans; }
*以上的Base尽量取接近字符串所含字符类型数的质数,这样可以尽量避免
0x06. 数据溢出的问题
实际操作发现,多项式
可以考虑以下几种解决方案:
1.自然溢出 Hash
即将
代码实现:
const int Base = 133; unsigned long long get_Hash(string S, int L) { unsigned long long ans = 0; for (int i = 0; i < L; i++) { ans = ans * Base + S[i]; } return ans; }
2.大模数 Hash
即将每次得到的
代码实现:
const int Base = 133; const int Mod = 1e9 + 7; int get_Hash(string S, int L) { int ans = 0; for (int i = 0; i < L; i++) { ans = ((long long)(ans * Base) + S[i]) % Mod;//*ans * Base可能超出int上限,当成long long类型处理 } return ans; }
0x07. Hash Killer
实际上,以上的
简而言之,就是根据0x04.
详见OI wiki卡大模数Hash和卡自然溢出Hash
所以,如何优化Hash使其更进一步降低冲突率?
0x08. 双值 Hash
考虑每次用不同的
代码实现:
const int Base1 = 133, Base2 = 137; const int Mod1 = 1e9 + 7, Mod2 = 998244353; int get_Hash_1(string S, int L) { int ans = 0; for (int i = 0; i < L; i++) { ans = ((long long)(ans * Base1) + S[i]) % Mod1; } return ans; } int get_Hash_2(string S, int L) { int ans = 0; for (int i = 0; i < L; i++) { ans = ((long long)(ans * Base2) + S[i]) % Mod2; } return ans; }
0x09. 例题
好了,到这一步,字符串
P3370 【模板】字符串哈希
分析:哈希板子,题面没有明确说出是否卡
被卡了。。
所以只能老老实实写双
代码实现(暴力匹配):
#include<bits/stdc++.h> using namespace std; const int Mod1 = 1e9 + 7, mod2 = 998244353; const int base1 = 137, base2 = 131; int get_hash_1(string s, int L) { int ans = 0; for(int i = 0; i < L; i++) { ans = ((long long)ans * base1 + s[i]) % Mod1; } return ans; } int get_hash_2(string s, int L) { int ans=0; for(int i = 0; i < L; i++) { ans = ((long long)ans * base2 + s[i]) % mod2; } return ans; } string str; int ans = 0, n; int s_hash1[10005], s_hash2[10005]; int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++){ cin >> str; s_hash1[i] = get_hash_1(str, str.length()); s_hash2[i] = get_hash_2(str, str.length()); bool fg = true; for(int j = i-1; j >= 1; j--){ if(s_hash1[i] == s_hash1[j] && s_hash2[i] == s_hash2[j]){fg = false; break;} } if(fg) ++ans; } printf("%d", ans); return 0; }
双哈希就可以AC了!。
本文作者:Ydoc770
本文链接:https://www.cnblogs.com/Ydoc770/p/18366191
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步