数据结构(邓俊辉)学习笔记】串 17——Karp-Rabin算法:散列

news2024/12/25 23:48:39

文章目录

  • 1.数位溢出
  • 2.散列压缩
  • 3.应对冲突
  • 4.指纹更新

1.数位溢出

在这里插入图片描述

在前一节中,已经成功地完成了一次视角转换,了解到应该如何从数学上将每一个串视作为一个自然数。以下我们就来将这一构思具体的兑现为一个算法。很有意思的是,我们在此需要用到第9章的关键技术散列

我们将每一个串所对应的自然数称作为它的指纹 fingerprint。因这个数相对于串,就像指纹相对于人一样,可以用来甄别其身份。然而我们注意到,这样一个自然数是以字符集的规模作为进制的,因此字符集只要不是很小,这类指纹的数值就会变得很大,这不能不说是个坏消息。

比如,我们知道对于 ASCII 字符集来说,它的规模为128,对于这类字符,即便模式串的长度不是特别的长,它对应的指纹也会长的令我们吃不消。我们可以来做一个快速的封底估算,128 是 2 的7 次方,因此即便是长度为 10 的模式串,它所对应的指纹也至少需要70个比特方能表示。
  ~  
这就意味着,即便在64位的计算平台上,长度不小于10的字符串将无法直接表示。而更糟糕的是,我们因此而遇到的麻烦还不止这些。

实际上,在整数的字宽已经不能继续视作为常数之后,整数之间的运算,也不能继续保证可在常数时间内完成。尽管 RAM 模型曾经的确做过这样一个不切实际的假设。实际上就渐进复难度的意义而言,此时,每次指纹比对所需要的时间将仍然线性正比于串的长度,也就是说,我们计算效率将重新退化到蛮力算法的水准。

那么,为了破解这一难题,你又能想到哪些高招呢?

2.散列压缩

在这里插入图片描述

既然以上问题的根源在于数位的溢出,那么我们很自然的就会应该想到通过压缩来解决它。没错,将一个硕大的取值空间压缩到一个可存储、可计算的更小的空间。从方法论上讲,这也不正是散列吗?

没错,我们需要对指纹来做散列压缩。具体地,我们将借助合适的散列函数将字符串的指纹压缩到存储器可支持的范围

这里我们不妨就以模余法为例,模余的基底取作素数 97。接下来,假设我们需要在这样一个文本串中寻找模式串 82818, 以下的主体流程与蛮力算法一样:

  1. 我们也是从首个对齐位置开始,逐次地去尝试各个位置,在每一个位置我们都将局部的子串与模式串进行比对。

    而在这里最为本质的不同在于,我们将不再是逐个地去比较每个字符,而是直接在两个串的指纹之间进行比对。

  2. 为此我们首先要计算出模式串的指纹,如果没有算错,应该是77。在文本串中,我们首先尝试的是子串 27182。也不难验证,它所对应的指纹为22,与目标指纹77不符,因此我们可以随即将其排除并接着转向下一个字串,也就是71828。

  3. 同样地,也不难验证,它的指纹为48。与77不符,所以也同样被排除掉。

  4. 再接下来对应的字串为 18281,它所对应的指纹也不难验算为45,同样与77不符,因而可以排除掉。

  5. 不难看出,整个算法将在下一个对齐位置发现匹配。是的,这个子串与模式串完全一致,所以它所对应的指纹也应该为77,而实际上这也是算法所发现的。

于是通过这种方法,我们也同样地完成了一次模式匹配。需要提醒你再次注意的是,在整个这样的计算过程中,我们分别只需要常数的时间就可以排除或者确认一个对齐位置,而这一点正是我们设计这种算法的初衷

当然,算法至此,依然没有尽善尽美,你能看出其中的问题吗?

3.应对冲突

在这里插入图片描述

没错,冲突。既然是散列,冲突就必然不可避免。

不过好在,我们这里只用到了指纹相等置于匹配的必要性,这与散列中的试探过程完全同理

你应该记得在试探查找的过程中,即便我们发现对应的桶非空,我们也不会贸然地认为它必定就是我们要找的元素。是的,为了最终确定它的身份,我们还需要做一次严格的比对。而在这里,我们也不妨沿用这种方法。

来看这样一个实例:依然是刚才我们已经熟知的那个文本串,只不过这里将模式串替换为18284。

  1. 首先确认,模式串的指纹为48。

    以下,我们依然是去逐个尝试每一个对齐位置。

  2. 在第一个对齐位置,我们得到的是指纹 22,既然它与目标的 48 不等,我们也可自然地将这一对齐位置排除掉。

  3. 接下来的第二个对齐位置,所对应的局部子串是 71828。很显然,它并不是我们要找的模式串。然而很不幸,它所对应的指纹却是 48,与模式串的指纹一模一样。当然,这也没有什么奇怪的,两个不同的元素在经过散列变换之后,有可能会被映射到同一个散列码,这种现象正是我们所谓的冲突。

    好在,我们沿用了散列表的策略,我们在这种情况下还会对这两个字符串做逐位的比对,以最终确定它的确是匹配。当然经过这样的严格比对之后,我们也的确可以排除掉这个对齐位置。

事实上,这个算法会如此不断地继续运行下去,直到最终抵达真正的匹配穿,也就是18284。当然,我们算法在此也不会遗漏掉这次匹配,因为显然这个局部子串所对应的散列码也应该是48。

这样我们又再次向前迈出了坚实的一步,至此,我们距离 Karp-Rabin 算法只差最终的一小步了。

4.指纹更新

在这里插入图片描述

就目前的模式而言,为了计算出文本串中每一个子串所对应的指纹,我们所需要花费的时间似乎都需线性正比于子串的长度

当然也就是模式串的长度,如果考虑到有多达 O(n) 个这样的潜在子串,那么你或许会沮丧的发现,最终的整体时间复杂度又再一次回到了O(n) * m,难道,我们此前的心血都是白费的吗?这也是我们在最后这一小步中所要解决的一个关键问题。

我们思考的方向和目标是,将每一指纹的计算成本从 O(m) 降低到常数。如果你还没有想出有效的方法,不妨首先温习一下此前的进制转换算法,或许你能从中得到一些启示。

没错,在那个算法中,我们很好地利用了相邻数位之间的相关性。其实在这里,也存在着类似的相关性。在文本串中,任何两个相邻子串之间都存在着紧密的相关性。具体来说,二者的指纹之间存在的相关性,而这两个指纹的计算过程以及结果之间也存在着紧密的相关性。

从这幅图中可以看出,相邻的子串几乎一样,二者唯一的区别只在于前者的首字符以及后者的末字符。只要能够悟到这一点,相信你就不难设计出一种新的方法,使得我们可以在常数的时间内由前一个指纹得到后一个指纹。

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

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

相关文章

qt4.8.7编译中出现const void* 禁止转换为void *

编译错误 错误忘截图&#xff0c;大概是如下头文件的问题&#xff1a; 该文件中的这一段函数报了如图所示的错误&#xff1a; // Test and set for pointerstemplate <typename T> Q_INLINE_TEMPLATE bool QBasicAtomicPointer<T>::testAndSetOrdered(T *expecte…

Tensor常见操作、自动微分及手动构建模型

有关张量&#xff08;Tensor&#xff09;的创建、常见属性及数据转换 见此链接https://blog.csdn.net/m0_68153457/article/details/141686846?spm1001.2014.3001.5501 一、Tensor常见操作 1.tensor相关运算 在进行相关运算前&#xff0c;我们要学会获取元素的值&#xff0…

使用本地IP无法访问前端项目的问题

说明&#xff1a;记录一次使用本机IP无法访问前端项目的问题 场景&解决 前端项目在我本机电脑上部署完成&#xff0c;我想通过局域网的IP把地址发给测试&#xff0c;发现使用localhost和127.0.0.0都能访问到前端项目&#xff0c;但是这个地址只能在自己的电脑上访问。 解…

Java响应式编程

Java响应式编程是一种基于异步和非阻塞的编程范式&#xff0c;旨在处理现代应用中日益增长的高并发、海量数据以及低延迟需求。通过响应式编程&#xff0c;开发者能够以更加优雅和高效的方式编写能够应对这些挑战的应用程序。 Quick start import reactor.core.publisher.Flux…

认知杂谈44

今天分享 有人说的一段争议性的话 《耐心雕琢人生&#xff1a;终身学习&#xff0c;绘就多彩画卷》 嘿&#xff0c;咱都知道&#xff0c;老有人说二十一天就能养成个新习惯&#xff0c;听着好像挺容易挺快的哈。 I I 可实际上呢&#xff0c;靠这种速成法养出来的习惯&#xff…

uniapp生命周期函数

常见页面生命周期函数 onLoad(options): 页面加载时触发&#xff0c;可以接收启动页面时的参数 onShow: onReady: 页面初次渲染完成时触发。 onHide: 页面被隐藏时触发 onUnload: 页面被关闭时触发 onLoad作用 获取url传递的参数,可以使用onLoad来获取,具体实现可以查看 如何获…

MySQL进阶篇1

一、存储引擎 1.1 MySQL体系结构 1.2 存储引擎简介 存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的&#xff0c;而不是基于库的&#xff0c;所以存储引擎也可被称为表类型。 1.3 存储引擎特点 1.4 存储引擎选择 存储引擎没有好坏之分&am…

Ubuntu22.04安装 docker和docker-compose环境

Docker简介 Docker 是一个开源的应用容器引擎&#xff0c;它使开发者能够打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之间不会有任何接口&#xff08;…

深度学习入门笔记

深度学习入门笔记 感知机逻辑与门与非门或门多层感知机异或门 神经网络激活函数输出层设计损失函数均方误差 MSE交叉熵误差 反向传播算法计算图局部计算计算图反向传播反向传播 参数更新训练过程总结 该篇文章为本人学习笔记的一部分。笔记基于《深度学习入门 基于python理论实…

Navicat连接Mongodb成功了,但是无法显示数据库怎么办?

不知道你是否遇到过&#xff1f;Navicat连接Mongodb成功了&#xff0c;但是无法显示数据库怎么办&#xff1f; 解决办法 这个问题比较坑&#xff0c;对于第一次接触的小伙伴&#xff0c;可能会一脸懵逼&#xff0c;原因就是在Navicat中默认会不显示隐藏的项目&#xff0c;如果不…

产品/运营经理的数据分析思维和学习路径

数据分析是产品经理和运营同学必知必会的技能之一&#xff0c;该技能会贯穿到产品经理和运营同学的整个职业生涯。 产品经理不需要成为数据分析方面的专家&#xff0c;但分析哪些数据、如何分析数据、如何用数据辅助决策、如何用数据驱动业务&#xff0c;这些是产品和运营同学…

C++学习笔记之指针(基础)

C学习笔记之指针&#xff08;基础&#xff09; https://www.runoob.com/cplusplus/cpp-pointers.html C/C中的指针内容是早就盛名在外了&#xff0c;当然了&#xff0c;想要随心所欲地使用也是要做很多功课的&#xff0c;先简单了解下吧~ 首先&#xff0c;我们需要了解一点内存…

STM32基础篇:RTC × Unix时间戳 × BKP

Unix时间戳 最早是在Unix系统使用的&#xff0c;之后很多由Unix演变而来的系统也都继承了Unix时间戳的规定。目前&#xff0c;Linux、Windows、安卓这些系统&#xff0c;其底层的计时系统都是使用Unix时间戳。 Uinx时间戳&#xff08;Unix Timestamp&#xff09;定义为从UTC/…

论文速读|ReKep:空间时间理论的关系关键点约束,用于机器人操作

项目地址&#xff1a;ReKep | Spatio-Temporal ReasoningReKep | Spatio-Temporal Reasoning of Relational Keypoint Constraints for Robotic ManipulationReKep | Spatio-Temporal Reasoning ReKep&#xff08;Relational Keypoint Constraints&#xff09;是一种基于视觉的…

Nebula: 无服务器计算中的性能和能源效率 - WebAssembly与Docker的比较研究

这篇论文的标题是《Nebula: Performance and Energy Efficiency in Serverless Computing - A Comparative Study of WebAssembly and Docker》&#xff0c;作者是 Marius Nilsen Kluften&#xff0c;来自奥斯陆大学&#xff08;University of Oslo&#xff09;的信息学系。论文…

android仿assistivetouch悬浮窗实现(带功能实现)

一、悬浮窗点击后的界面&#xff1a; 主要有四个功能&#xff0c;返回、应用程序、退出和主界面。其他功能也可以类似添加。 界面布局代码就不贴出来了&#xff0c;源码&#xff08;切记需要签名才能让功能实现&#xff09;&#xff1a;下载地址 二、主要是检测系统启动或者a…

时序数据库荣登巅峰,被央视报道了!

8月30日&#xff0c;事务处理性能委员会TPC正式公布了最新的国际权威数据库性能基准榜单&#xff0c;“清华系”发起研制的Apache IoTDB开发的国产化时序数据库软件TimechoDB&#xff0c;在性能和系统成本维度上双双打破世界纪录。在央视《24小时》节目中&#xff0c;1分34秒重…

《黑神话:悟空》与游戏经济学的深度剖析

《黑神话&#xff1a;悟空》作为近年来备受瞩目的国产3A游戏大作&#xff0c;自其发布以来&#xff0c;不仅在游戏界内引起了轰动&#xff0c;更在多个消费领域产生了深远的影响。这款游戏不仅以其卓越的品质和深刻的文化内涵吸引了大量玩家的关注&#xff0c;还通过一系列连锁…

神策埋点 sensorsdata.es6.min.js、sensorsdata.min.js 触发eslint 语法检查,导致打包不成功

问题描述&#xff1a; 在使用神策埋点时&#xff0c;下载的web js sdk&#xff0c;打包时eslint 语法检查&#xff0c;会导致打包不成功。npm start没问题。 主要错误是&#xff1a; Line 1:204272: Expected an assignment or function call and instead saw an expression …

[Python]之深拷贝与浅拷贝

Python之深拷贝与浅拷贝 概述: ​ 大白话解释就是 深拷贝拷贝的多, 浅拷贝拷贝的少. 深浅拷贝区别就是: 拷贝的层级的多与少. 深浅拷贝都能操作可变类型 和 不可变类型, 但是深浅拷贝一般操作的都是 可变类型, 几乎不会出操作不可变类型的. 可变类型 和 不可变类型的划分依…