AcWing 841. 字符串哈希

news2024/9/23 5:34:24

题目描述


分析:

字符串 hash 小试牛刀

我们在之前模拟散列时,设置的哈希函数为将一个元素(element, e)输入哈希函数中,输出是一个整数,而那时的 e e e 为一个有范围的整数。现在我们考虑更复杂的情形, e e e 为一个字符串,为了区分,记为 E E E,字符串哈希函数就是将一个字符串 E E E 映射为一个整数,使得该整数可以尽可能地代表一个字符串 E E E
假设字符串 E E E 由大写字母 A − Z A-Z AZ 和小写字母 a − z a-z az 构成,在这个基础上,我们可以对字母进行编码: A − Z A-Z AZ 1 − 26 1-26 126 a − z a-z az 27 − 52 27-52 2752,这样就把大小写字母对应到了五十三进制中。接着,按照将五十三进制转换为十进制的思路,由进制转换的结论可知,在进制转换中,得到的十进制是唯一的,由此便可将字符串映射成整数的需求。这里转换成的整数最大为 5 3 l e n g t h − 1 53^{length}-1 53length1(进制转换的基本性质), l e n g t h length length 为字符串的长度。实现代码如下:

// 哈希函数 h() 本质即为进制转化
int h(char* E)
{
	int ans = 0;
	for (int i = 0; E[i]; i ++)
	{
		if (E[i] >= 'A' && E[i] <= 'Z') ans = ans * 53 + (E[i] - 'A');
		else if (E[i] >= 'a' && E[i] <= 'z') ans = ans * 53 + (E[i] - 'a') + 26;
	}
	return ans;
}

假设字符串为"BCDE",进行哈希映射后得到的整数为 5 ∗ 5 3 0 + 4 ∗ 5 3 1 + 3 ∗ 5 3 2 + 2 ∗ 5 3 3 5*53^0+4*53^1+3*53^2+2*53^3 5530+4531+3532+2533。从代码和样例中可以看出为什么我们在之前将 A − Z A-Z AZ 设为 1 − 26 1-26 126 而不是 0 − 25 0-25 025,这是因为如果 “A” 为 0 0 0,那么 “AA”、“AAA” 都为 0 0 0 了。

字符串 hash 进阶

对于上面的分析,我们可以总结出一个更系统的式子:
S [ i ] = S [ i − 1 ] ∗ p + E [ i ] S[i]=S[i-1]*p+E[i] S[i]=S[i1]p+E[i]
其中 p p p 为我们所采取的进制, E [ i ] E[i] E[i] 表示字符串的第 i i i 位是什么, S [ i ] S[i] S[i] 表示字符串的前 i i i 个字符的子串的 hash 值。这样,当 i i i 取遍 1 − l e n g t h 1-length 1length 后, S [ l e n g t h ] S[length] S[length] 就是整个字符串的哈希值,而其他位置保存了部分字串的 hash 值。注意这里没有了 c o n v e r t ( ) convert() convert() 函数,因为我们打算直接是用字符串字符的 ASCII 码 ,因此 p p p 进制的选择就扑朔迷离了。
在转换过程中,字符串与整数是一一对应的,但由于没有适当的处理,当字符串字符较长时,产生的数会非常大,没办法用一般的数据类型存储。因此,按照之前对哈希的理解,要进行取模,即:
S [ i ] = ( S [ i − 1 ] ∗ p + E [ i ] ) % k S[i]=(S[i-1]*p+E[i])\%k S[i]=(S[i1]p+E[i])%k
通过这种方式可以把字符串转换成范围上能接受的整数。但这可能又产生另外的问题,也就是 hash值产生冲突。而根据y总的经验值, p p p 133 133 133 13331 13331 13331 k k k 2 64 2^{64} 264,在 99.99 % 99.99\% 99.99% 的情况下是不会产生冲突的。因此这和普通哈希是有区别的,普通哈希是可以处理冲突的,而字符串哈希是不考虑冲突的(无法解决?)

子串的 hash值

考虑求解子串的 hash值,也就是求解 S [ l . . . r ] S[l...r] S[l...r]。由于上面介绍的取模运算在括号最外层,而我们接下来需要考虑括号内的问题,所以先暂时把取模运算放在一边,简化讨论。即:
S [ i ] = ( S [ i − 1 ] ∗ p + E [ i ] ) S[i]=(S[i-1]*p+E[i]) S[i]=(S[i1]p+E[i])
子串的 hash值, S [ l . . . r ] S[l...r] S[l...r] 实际上等于把字符串 E [ l . . . r ] E[l...r] E[l...r] p p p 进制转换为十进制,也就是如下式子所表示的:
S [ l . . . r ] = E [ l ] ∗ p r − l + E [ l + 1 ] ∗ p r − l − 1 + . . . + E [ r ] ∗ p 0 S[l...r]=E[l]*p^{r-l}+E[l+1]*p^{r-l-1}+...+E[r]*p^0 S[l...r]=E[l]prl+E[l+1]prl1+...+E[r]p0
看到这个式子有没有觉得头大呢?没关系,这是很正常的,因为它有许多的参数需要理清,先看一个实例:
在这里插入图片描述
上面的 S [ l . . . r ] S[l...r] S[l...r] 的公式可以通过 S [ j ] S[j] S[j] 推导出:
S [ r ] = S [ r − 1 ] ∗ p + E [ r ] S[r]=S[r-1]*p+E[r] S[r]=S[r1]p+E[r]
= ( S [ r − 2 ] ∗ p + E [ r − 1 ] ) ∗ p + E [ r ] \qquad =(S[r-2]*p+E[r-1])*p+E[r] =(S[r2]p+E[r1])p+E[r]
= S [ r − 2 ] ∗ p 2 + E [ r − 1 ] ∗ p + E [ r ] \qquad =S[r-2]*p^2+E[r-1]*p+E[r] =S[r2]p2+E[r1]p+E[r]
= . . . \qquad = ... =...
= S [ l − 1 ] ∗ p r − l + 1 + E [ l ] ∗ p r − l + . . . + E [ r ] ∗ p 0 \qquad = S[l-1]*p^{r-l+1}+E[l]*p^{r-l}+...+E[r]*p^0 =S[l1]prl+1+E[l]prl+...+E[r]p0
= S [ l − 1 ] ∗ p r − l + 1 + S [ l . . . r ] \qquad = S[l-1]*p^{r-l+1}+S[l...r] =S[l1]prl+1+S[l...r]
移项可以得到:
S [ l . . . r ] = S [ r ] − S [ l − 1 ] ∗ p r − l + 1 S[l...r]=S[r]-S[l-1]*p^{r-l+1} S[l...r]=S[r]S[l1]prl+1
于是就得到了子串 E [ i . . . j ] E[i...j] E[i...j] 的 hash值 S [ i . . . j ] S[i...j] S[i...j],加上原来的取模操作可以得到:
S [ l . . . r ] = ( S [ r ] − S [ l − 1 ] ∗ p r − l + 1 ) % k S[l...r]=(S[r]-S[l-1]*p^{r-l+1})\%k S[l...r]=(S[r]S[l1]prl+1)%k
由于C++有 unsigned long long(ULL) 类型表示范围为 [ 0 , 2 64 − 1 ] [0,2^{64}-1] [0,2641],因此当需要对 2 64 2^{64} 264 取模时,可以用 ULL 类型存储该数,当出现溢出时就相当于进行了取模运算


代码(C++)

#include <iostream>

using namespace std;

typedef unsigned long long ULL;

const int N = 100010, P = 131;
char E[N];
// POW 数组进行提前打表操作,i 中记录了 p^i
ULL H[N], POW[N];

//返回值为 ULL,相当于进行了取模运算
ULL subhash(int l, int r)
{
    // P 的 r-l+1 次幂,存储在对应下标中
    return H[r] - H[l - 1] * POW[r - l + 1];
}

int main()
{
    int n, m;
    // 对于字符串 E,从下标为 1 的位置读入
    scanf("%d%d%s", &n, &m, E + 1);
    
    POW[0] = 1;
    
    for (int i = 1; i <= n; i ++)
    {
        // H[i] 表示字符串前 i 个字符的子串 hash值
        H[i] = H[i - 1] * P + E[i];
        // 打表幂运算
        POW[i] = POW[i - 1] * P;
    }
    
    while (m --)
    {
        int l1, r1, l2, r2;
        scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
        
        if (subhash(l1, r1) == subhash(l2, r2)) puts("Yes");
        else puts("No");
    }
    
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/351471.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

论文笔记NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis

NeRF使用神经网络来表示场景。给定一个场景&#xff0c;输入该场景稀疏的视角图片&#xff0c;NeRF可以合成该场景新的视角的图片。 神经辐射场 神经辐射场&#xff08;neural radiance field&#xff0c;NeRF&#xff09;使用5D的向量值函数表示一个场景。 输入是连续的5D坐…

LeetCode 160. 相交链表

原题链接 难度&#xff1a;easy\color{Green}{easy}easy 题目描述 给你两个单链表的头节点 headAheadAheadA 和 headBheadBheadB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 nullnullnull 。 图示两个链表在节点 c1…

分享12款我常用的开源免费软件

嗨,大家好, 我是徐小夕, 在一个劳动力追求高性价的时代, 我们无时无刻不在追寻更高的工作学习效率, 提高工作舒适度,接下来我就和大家分享一下我之前用过的, 能提高工作效率的开源免费工具.1. 修图&#xff1a;Krita 【开源】image.png地址: https://krita.org/zh/Krita 是一款…

CK-GW06-E03与欧姆龙PLC配置指南

CK-GW06-E03与欧姆龙PLC配置指南CK-GW06-E03是一款支持标准工业EtherCAT协议的网关控制器,方便用户集成到PLC等控制系统中。本控制器提供了网络 POE 供电和直流电源供电两种方式&#xff0c;确保用户在使用无POE供电功能的交换机时可采用外接电源供电&#xff1b;系统还集成了六…

Mac环境下安装MongoDB数据库

一、下载安装 1.1 下载MongoDB 首先&#xff0c;从MongoDB官网下载自己想要使用的版本。 解压缩下载的压缩包重命名为mongodb&#xff0c;将mongodb文件夹复制到/usr/local目录下。 brew tap mongodb/brew brew install mongodb-community4.41.2 配置环境变量 打开.zshrc…

python 获取chrome浏览器的安装目录,即chrome的绝对路径

实现目标需要使用到&#xff08;引包|倒包&#xff09; 2 个 python 模块&#xff0c; import win32api import win32con 引言&#xff08;扉页&#xff09;&#xff1a; 也适用于查找安装在Windows中的应用程序的绝对路径&#xff0c; 譬如&#xff1a;微信&#xff08;WeChat…

APP自动化(1)-Appium中adb常用命令

1.建立Appium与手机模拟器的连接环境 首先确定的是Appium已经打开和手机模拟器已经打开并且appium点击了开始的按钮&#xff0c;然后用cmd进去win的dos系统&#xff0c; 输入tasklist找到这个进程号&#xff0c;每台电脑都是不一样的进程号要按照你自己的来 查询pid地址netst…

Git 构建分布式版本控制系统

版本控制概念Gitlab部署1.版本控制概念 1.1分类 &#xff08;一&#xff09;1 本地版本控制系统&#xff08;传统模式&#xff09; &#xff08;二&#xff09;2 集中化的版本控制系统 CVS、Subversion&#xff08;SVN&#xff09; &#xff08;三&#xff09;3 分布式…

linux程序分析工具

嵌入式调试工具1. nm2. addr2line3. readelf3.1 ELF 文件分类3.2 ELF文件组成3.3使用1. nm nm源于name&#xff0c;是linux下一个文本分析工具&#xff0c;可以罗列指定文件中的符号(函数名、变量&#xff0c;以及符号类型)。 nm命令参数如下&#xff1a; 用法&#xff1a;nm …

深入理解MySQLⅢ -- 锁与InnoDB引擎

文章目录锁概述全局锁表级锁表锁元数据锁意向锁行级锁行锁间隙锁&临键锁InnoDB引擎逻辑存储结构架构内存结构磁盘结构后台线程事务原理redo logundo logMVCC锁 概述 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#x…

Fiddler安装教程、工具图解

目录 一、Fiddler是什么&#xff1f; 二、Fiddler原理 三、Fiddler安装 Fiddler界面介绍 四、常用的工具图解 4.1会话列表 4.2状态栏 4.3内容显示区 一、Fiddler是什么&#xff1f; Fiddler是客户端与服务器之间的HTTP代理&#xff0c;是当前最常用的HTTP协议抓包工具。 …

谈谈 《 JavaScript - DOM编程艺术 》这本书

前言 好吧&#xff0c;现在已经2023年了&#xff0c;对于这本书&#xff08;第二版&#xff09;来说可能有点老了&#xff0c;这本书不是很难理解&#xff0c;但也不是很适合新手读&#xff0c;当然&#xff0c;这本书并不是百宝书 &#x1f4d5; &#xff0c;它更注重于编程的…

格式化字符串你都懂了吗

文章目录前言一、什么是格式化字符串&#xff1f;二、使用 % 格式化字符串三、使用 format() 格式化字符串总结前言 今天跟大家聊聊字符串的格式化这部分内容。乍一听“格式化”这三个字&#xff0c;有的初学者可能会懵&#xff1a;难道这是要清空字符串的节奏&#xff1f; 其…

网络管理之设备上线技术的发展现状和趋势

网络和网络设备无处不在 随着社会的发展和技术的进步&#xff0c;人类文明开始向信息时代演进&#xff0c;网络逐渐变成现代社会不可或缺的一部分&#xff0c;极大程度影响了人类的认知形式、思维方式与生活模式。从家庭网&#xff0c;到企业网&#xff1b;从无线网&#xff0…

Mysql MHA搭建

. 目录 机器配置 安装Docker Docker安装和启动Mysql8.0.26 搭建Mysql一主二从 设置三台机器免密访问 安装MHA 搭建问题记录 问题1 MHA验证主从复制报错Access denied; you need (at least one of) the SUPER, REPLICATION CLIENT privilege(s) for this operation 问题2 MHA验证…

doris - 数仓 拉链表 按天全量打宽表性能优化

数仓 拉链表 按天全量打宽性能优化现状描述优化现状描述 1、业务历史数据可以变更 2、拉链表按天打宽 3、拉链表模型分区字段设计不合理&#xff0c;通用的过滤字段没有作为分区分桶字段 4、拉链表表数据量略大、模型数据分区不合理和服务器资源限制&#xff0c;计算任务执行超…

安装JupyterLab失败的解决方案

由于本人电脑安装的Python版本3.6比较低&#xff0c;所以可能存在下面两种方法都安装失败&#xff0c;最后给出一个简单省事的方法。Jupyter lab比Jupyter Notebook要好用&#xff0c;试了之后感觉跟VSCode一样的存在&#xff0c;所以还是值得安装来代替Jupyter Notebook使用。…

Ae:解释素材

所谓解释素材 Interpret Footage&#xff0c;就是通过修改素材的某些属性&#xff08;像素长宽比、帧速率、颜色配置文件及 Alpha 通道类型等&#xff09;&#xff0c;让它能更好地参与到合成中去。Ae菜单&#xff1a;文件/解释素材快捷键&#xff1a;Ctrl Alt G在项目面板里…

【C语言】编程初学者入门训练(13)

文章目录121. 小乐乐算最高分122. 小乐乐计算求和123. 小乐乐计算函数124. 小乐乐查找数字125. kiki学程序设计基础126. kiki算期末成绩127. kiki说祝福语128. kiki的最高分129. 求质数的个数130. kiki去重整数并排序121. 小乐乐算最高分 问题描述&#xff1a;小乐乐的老师BoB…

4.2 双点双向路由重发布

1. 实验目的 熟悉双点双向路由重发布的应用场景掌握双点双向路由重发布的配置方法2. 实验拓扑 双点双向路由重发布如图4-6所示: 图4-6:双点双向路由重发布 3. 实验步骤 IP地址的配置R1的配置 <Huawei>system-v…