哈希学习笔记
upd:好像写的很杂糅。。。懒得改了。。。
板:P3370 【模板】字符串哈希
复杂度:O(n)
用途:将一串字符串映射到一个数
怎么写?
选取一个较小质数 p 选取一个较大质数 mod1
将字符串转换成一个 p 进制在 mod1 定义下的数
常见模数:1e9+7(但很容易被卡)
不太容易被卡的模数:19260817 123456791
写法如下:
哈希冲突
根据哈希的写法 我们不难发现 相同的字符串的 hash 值一定相等
但 hash 值相等的字符串一定相同吗? 其实不然
事实上 是有可能存在两个不同字符串转换后的 hash 值相同的情况 我们将其称之为哈希冲突
这里给出结论 若字符串的长度为
那么如何避免 hash 冲突呢?
1.unsigned long long
unsigned long long 的数据范围为
缺点:有概率会被丧心病狂的出题人卡
2.双哈希
顾名思义 就是准备两个模数 做两遍哈希
优点:我们认为是无法被 hack 的
缺点:常数巨大 取模和除法运算又很慢 有可能慢到 TLE
3.哈希表
优点:安全
缺点:我不会写 写起来有点麻烦
4.unordered_map
有点:写起来非常方便
缺点:STL...懂得都懂
同时 哈希还支持查询子串的 hash 值
需要 O(n) 的复杂度 O(1) 查询
首先我们要开一个数组 把之前每一步的 ch 记录下来
用ch[i] 表示 p 的 i 次方
我们以十进制数为例 观察如何查询中间一段数字的值
如这段:123456789 我们要查询第 3 位至第 7 位 即34567
我们已知每一段前缀的 hash 值 不难发现我们要用到 hash[2] = 12 与 hash[7] = 1234567
然后我们发现
根据这个式子 我们可以推广出
具体代码如下:
二维哈希
才发现之前没写 补一个(
对于预处理 我们横着 hash 一遍再竖着 hash 一遍:
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) ha[i][j] = ha[i][j - 1] * p1 + a[i][j];
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) ha[i][j] += ha[i - 1][j] * p2;
}
对于查询
code:
inline int query(int x1, int y1, int x2, int y2) {
return ha[x2][y2] - ha[x1 - 1][y2] * ch2[x2 - x1 + 1] - ha[x2][y1 - 1] * ch1[y2 - y1 + 1] + ha[x1 - 1][y1 - 1] * ch1[y2 - y1 + 1] * ch2[x2 - x1 + 1];
}
hash 与 kmp 的区别:kmp 主要对于原字串的不同前缀问题较为好用 而 hash 则更加适用于多次询问 每次查询原字串中间一段区间的问题
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探