当前位置: 首页 > news >正文

20251115 - Hash

前言

为什么此次题单不叫字符串 hash 呢?

应该搞点 [哈希表](P11615 【模板】哈希表 - 洛谷) 的!

概念

哈希,就像是把一个很大的东西,映射到一个小盒子里,这个盒子就是哈希表

字符串哈希,顾名思义,就是把很 long 的字符串,映射到很 short 的范围里。:

当然,不保证正确性。

性质

  1. 在 hash 函数值不一样的时候,两个字符串一定不一样;

  2. 在 hash 函数值一样的时候,两个字符串不一定一样。

如果出现字符串不相同但 hash 值一样,就称为哈希冲突

防止 \(hash\) 冲突的方法

其实 \(hash\) 冲突无法防止,但可以降低冲突的概率。

  1. 取一个较大的质数,比如 \(1e9 + 7\)\(114514\)\(2^{62}-1\) 等……
  2. \(base\) 尽量随机,防止出题人卡你 \(hash\)

用法

如何求出 hash 值呢?还是用 unordered_map吧

  1. 自然溢出

利用 \(unsigned \ long \ long\) 的特性,可以不用模数了!

\(base\) 一般取值 \(2^{61} - 1\)

const ull base = (1ULL << 61) - 1;
ull get_hash(char s[]){int len = strlen(s + 1);vector<int>ve(len + 1);for(int i = 1;i <= len;i++){ve[i] = ve[i - 1] * base + s[i];}return ve[len];
}
  1. 模数哈希

自然溢出实际上是对 \(2^{62} - 1\) 取模,而模数哈希就是换了个模式而已!

const int base = 131; // 一定要大于字符集
const int P = 998244353;
int get_hash(char s[]){int len = strlen(s + 1);vector<int>ve(len + 1);for(int i = 1;i <= len;i++){ve[i] = (1LL * ve[i - 1] * base) % P + s[i] % P;ve[i] %= P;}return ve[len] % P;
}
  1. STL

C++ 的标准模版库(STL)有一个好用的哈希表,他叫 unordered_map,目前仅仅基本数据结构!

unordered_map<int,int>mp;
void solve(){for(int i = 1;i <= n;i++){int x,y;read(x,y);mp[x] = y;}
} // 和map几乎一致

区间截取字符串

众所不周知,字符串匹配可以用 KMP 算法,但哈希也不是不行。

想想前缀和是什么样子的 s[r] - s[l - 1],那么 hash 也可以做减法!

int h[N],power[N];
void init(){power[0] = 1;for(int i = 1;i <= n;i++)h[i] = (1LL * h[i - 1] * base) % P + s[i] % P;for(int i = 1;i <= n;i++)power[i] = power[i - 1] * base;
}
int get_hash(char s[],int l,int r){int x = 1LL * h[l - 1] * power[r - l + 1] % P;return (h[r] - x + P) % P;
}

这样,我们就可以开心的字符串匹配了!

例题

P3370 【模板】字符串哈希

友情提醒:如果真的想好好练习哈希的话,请自觉。

此题即为字符串哈希的模版题!

计算字符串的 \(hash\) 值时,我们一般把原串的 \(B\) 进制数作为它的 \(hash\) 值,这样比较方便计算,并且预处理之后,也可以 \(O(1)\) 求出任意一个子串的 \(hash\) 值。

建议别用自然溢出,不然被出题人卡了就老实了!

字符串的长度一般会很长, \(int128\) 都存不下,怎么办?

高精度 取模!

建议自己手写 \(add\)\(mul\) 函数,不然被出题人卡了就老实了

注意!!注意!!别 #define int __uint128_t !!!!!

代码:

#include <bits/stdc++.h>using namespace std;
#define ll long long
const int N = 1e5 + 7;
const int P = 998244353;
const int inf = (1 << 30);
const int base = 173397;
template<class T> void read(T &x){x = 0;int f = 1;char ch = getchar();while(!(ch >= '0' && ch <= '9')){if(ch == '-') f = -f;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}x *= f;
}int n;
int get_hash(const string &x){int len = x.size(),res = 0;for(int i = 0;i < len;i++){res = ((ll)res * base) + x[i] % P;res %= P;}return res;
}
int a[N];
string s;
int main(){read(n);for(int i = 1;i <= n;i++){cin >> s;a[i] = get_hash(s);}int cnt = 0;for(int i = 1;i <= n;i++){bool ok = false;for(int j = i + 1;j <= n;j++){if(a[i] == a[j]) ok = 1;}if(!ok) cnt++;}printf("%d\n",cnt);return 0;
}
// 由于是vscode写的,所以排版有亿点不好

总结:hash 虽然很多能被某 \(STL\) 给水掉,但他确实很重要,所以……

友情提醒:如果真的想好好练习哈希的话,请自觉。—— HansBug

后记

哦,我知道了,以后还是用 hash map 吧!

注:unordered_map 非常容易被卡,还不如手写哈希!

http://www.jsqmd.com/news/41313/

相关文章:

  • apache和nginx解析php和lnmp和lamp搭建
  • hippy字节都在用的前端主流框架
  • springboot多模块报错分析(一) - f
  • 身为大厂前端的你,不能不知道Babel + Polyfill!
  • 跨域问题解决方案汇总
  • Access-Control-Allow-Origin 在企业中的用法
  • VUE_basic - Ref
  • 详细介绍:像素退场,曲线登场:现代响应式 CSS 全家桶 | 领码课堂
  • HTTPS 究竟比 HTTP 好在哪?
  • 小苯的因子查询
  • 详细介绍:MongoDB 自动化脚本安装方案
  • Linux网络--6、网络层 - 详解
  • 原型理解从入门到精通
  • 2025-11-15
  • 2025.11.15博客
  • Pandas - read_html()
  • 实用指南:Linux企业级解决方案架构:字节跳动短视频推荐系统全链路实践
  • 实用指南:PyTorch DataLoader 高级用法
  • 简单做一个舒尔特方格小游戏
  • C语言新手怎么快速掌握
  • RSS and Atom
  • Wi-Fi FTM(Fine Timing Measurement)简介
  • 通用会话控制方案
  • LISTAGG 用于将多行数据聚合为单行字符串(拼接),而与其功能相反的需求是 将单行字符串按指定分隔符拆分为多行数据
  • ESP32 I2S音频总线学习笔记(八):添加按键控制功能 - 详解
  • 2025年8款AI论文写作神器推荐:轻松搞定毕业论文查重
  • 基于python的酒店管理系统_36rhk752(Pycharm Flask Django成品源码LW) - 详解
  • pythontip 从字典中删除一组键
  • Softmax 函数全面而详细的解读,原理、图像、应用 - 详解
  • 中级前端工程师详细技能清单