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

洛谷P3370字符串哈希(集合:Hash表)

P3370 【模板】字符串哈希

题目描述

如题,给定 NNN 个字符串(第 iii 个字符串长度为 MiM_iMi,字符串内包含数字、大小写字母,大小写敏感),请求出 NNN 个字符串中共有多少个不同的字符串。

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

输入格式

第一行包含一个整数 NNN,为字符串的个数。

接下来 NNN 行每行包含一个字符串,为所提供的字符串。

输出格式

输出包含一行,包含一个整数,为不同的字符串个数。

输入输出样例 #1

输入 #1

5
abc
aaaa
abc
abcc
12345

输出 #1

4

说明/提示

数据范围

对于 30%30\%30% 的数据:N≤10N\leq 10N10Mi≈6M_i≈6Mi6Mmax⁡≤15M_{\max}\leq 15Mmax15

对于 70%70\%70% 的数据:N≤1000N\leq 1000N1000Mi≈100M_i≈100Mi100Mmax⁡≤150M_{\max}\leq 150Mmax150

对于 100%100\%100% 的数据:N≤10000N\leq 10000N10000Mi≈1000M_i≈1000Mi1000Mmax⁡≤1500M_{\max}\leq 1500Mmax1500

样例说明

样例中第一个字符串 abc\tt{abc}abc 和第三个字符串 abc\tt{abc}abc 是一样的,所以所提供字符串的集合为 {aaaa,abc,abcc,12345}\{\tt{aaaa},\tt{abc},\tt{abcc},\tt{12345}\}{aaaa,abc,abcc,12345},故共计 444 个不同的字符串。

题解

#include<iostream>
#include<string>
#include<vector>
#define Max 1050   //字符串的最大长度
#define base 257   //哈希计算的基数
#define mod 23333  // 哈希表的大小(取模值)
using namespace std;int n, ans;     //n:输入字符串的数量;ans:不同字符串的数量
string s;       //输入的字符串
vector<string> linker[mod + 2];  //哈希表主体,每个位置是一个字符串向量inline void insert() {int hash = 1;  //初始化哈希值//计算字符串的哈希值for (int i = 0; s[i]; i++) {hash = (hash * 111 * base) % mod;}string t = s;//检查当前哈希值对应的数组中是否已有该字符串(处理哈希冲突)for (int i = 0; i < linker[hash].size(); i++) {if (linker[hash][i] == t)return;   //已存在则直接返回,不重复计数}//不存在则插入哈希表并计数+1linker[hash].push_back(t);ans++;
}int main() {cin >> n;for (int i = 1; i <= n; i++) {cin >> s;insert();}cout << ans;return 0;
}

补充说明:

在哈希函数里,像代码中 hash = (hash * 111 * base + s[i]) % mod 这样选取 111 和 base 这类乘数,主要是为了让哈希值尽可能随机、均匀分布,以此降低哈希冲突概率,它们的选取一般要遵循这些原则:
1. 与字符集、模数适配
和字符的数值范围匹配:要是处理的是 ASCII 字符(范围 0 - 127 左右 )或者扩展 ASCII 字符(0 - 255 ),乘数选成能让字符数值参与运算后,哈希值变化更丰富的数。比如 base 选 261(大于常见字符集大小 ),和字符数值结合运算时,能让不同字符对哈希值的贡献更有区分度,避免 “字符数值小,乘以小乘数后变化微弱,容易撞哈希” 的情况。
和模数 mod 协同:尽量让乘数和 mod 互质(或者至少不是倍数关系 ),这样每一步哈希计算时,hash * 乘数 + 字符 的结果再取模,能让哈希值在 mod 范围内更均匀分散。像 mod 是 23333(质数 ),111 和 261 跟它互质的话,能减少哈希值扎堆,降低冲突概率。
2. 让哈希计算更 “发散”
增大哈希值差异:乘数要选成能放大不同字符、不同字符串位置差异的数。比如 111 是一个相对适中的数,每一轮哈希计算用它去乘之前的哈希值,再结合新字符,能让字符串里哪怕微小的差异(像不同位置换个字符 ),经过多轮运算后,哈希值差异被放大,让最终哈希结果区别更明显,不容易撞哈希。
避免 “乘法陷阱”:别选太小的乘数(比如 1、2 这类 ),否则不同字符或者不同字符串,哈希计算后结果容易趋同。也别选极端大的数(导致中间结果溢出、计算变慢 ,不过代码里用了取模,一定程度能缓解,但数值太大会让计算规律变弱,也可能有问题 ),要在 “区分度” 和 “计算可行性” 之间找平衡,111、261 就属于能较好制造差异,又不会让计算太离谱的数。
3. 适配实际场景与经验
经验性选取:很多时候,这类乘数是靠经验、或者测试调整出来的。比如在处理字符串哈希时,大家试过不同的基数(像 113、257、261 等 ),发现用这些数时,哈希冲突概率相对低,就会保留下来复用。111 可能也是经过实践,或者结合题目数据特点选的,用它能让代码在处理给定输入(比如题目里的字符串数据 )时,哈希表现更稳定。
适配数据特点:要是知道要处理的字符串有特定规律(比如全是小写字母、长度都很短等 ),可以针对性选乘数。比如全是小写字母时,乘数选成和 26(字母数量 )、256(ASCII 范围 )等有关联、又能制造差异的数,让哈希更适配数据,减少冲突。

http://www.dtcms.com/a/346401.html

相关文章:

  • Ubuntu解决makefile交叉编译的问题
  • 提升用户体验的交互设计实战指南:方法、流程与技巧
  • 在通义灵码中配置MCP服务
  • Linux--进程核心概念
  • 基于SamGeo模型和地图客户端的实时图形边界提取
  • 把 AI 变成「会思考的路灯」——基于自学习能耗模型的智慧路灯杆
  • Open3d:点对点ICP配准,点对面ICP配准
  • 105.QML实现现代Neumorphism风格界面01-Button实现
  • 如何提升科研能力:先停止“无效工作”,开始“有效科研”
  • 第二节阶段WinFrom-5:文件操作
  • 车载诊断架构 --- EOL引起关于DTC检测开始条件的思考
  • Linux822 shell:expect 批量
  • 《C++起源与核心:版本演进+命名空间法》
  • 易基因:Nat Commun/IF15.7:多组学研究揭示UHRF2在原始生殖细胞DNA甲基化重编程中的抗性调控机制
  • 光耦合器:电子世界的 “光桥梁“
  • Opnecv详细介绍
  • 量子计算基础
  • C#_组合优于继承的实际应用
  • 音视频处理工作室:实时通信的媒体层设计
  • 容器操作案例
  • C语言——内存函数
  • TTS文字合成语音芯片的使用场景
  • No module named blake2b
  • GaussDB GaussDB 数据库架构师修炼(十八)SQL引擎(1)-SQL执行流程
  • ODDR双边沿数据输出
  • 1小时检测cAMP的武功秘籍
  • AI 绘画争议背后:版权归属、艺术原创性与技术美学的三方博弈
  • Linux系统安装llama-cpp并部署ERNIE-4.5-0.3B
  • Unity--判断一个点是否在扇形区域里面(点乘和叉乘的应用)
  • Day2--HOT100--283. 移动零,11. 盛最多水的容器,15. 三数之和