当前位置: 首页 > 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.gsyq.cn/news/50646.html

相关文章:

  • 记录一次Windows复制粘贴不正常的情况
  • apache和nginx解析php和lnmp和lamp搭建
  • 跨域问题解决方案汇总
  • 详细介绍:像素退场,曲线登场:现代响应式 CSS 全家桶 | 领码课堂
  • HTTPS 究竟比 HTTP 好在哪?
  • 小苯的因子查询
  • Linux网络--6、网络层 - 详解
  • 原型理解从入门到精通
  • 简单做一个舒尔特方格小游戏
  • C语言新手怎么快速掌握
  • Wi-Fi FTM(Fine Timing Measurement)简介
  • LISTAGG 用于将多行数据聚合为单行字符串(拼接),而与其功能相反的需求是 将单行字符串按指定分隔符拆分为多行数据
  • Atcoder FPS 24 记录
  • 扩展单调栈扫描线维护历史信息
  • 酵母单杂交 (Y1H):蛋白质 - DNA 互作研究的 基因解码器
  • ORACLE行记录转字符串用分隔符连接的两个函数:WM_CONCAT、LISTAGG
  • 20232419 2025-2026-1 《网络与系统攻防技术》实验五实验报告
  • 第三十天
  • WinDbg 随笔 001 —— HelloWorld + WinDbg
  • C++篇(14)二叉树进阶算法题 - 详解
  • 2025年市场上品质好的羊毛地毯制造企业
  • 【STM32工程开源】基于STM32的人体健康监测环境
  • 实用指南:【C# OOP 入门到精通】从基础概念到 MVC 实战(含 SOLID 原则与完整代码)
  • tailwind自定义class问题小记
  • 2025年主流开源AI智能体框架平台概览 - 实践
  • Tarjan复建
  • 20251115
  • 20232307 2024-2025-1 《网络与系统攻防技术》实验五实验报告
  • EXECUTE IMMEDIATE语句分析
  • 产品更新与重构策略:创新与稳定的平衡之道 - 详解