【算法】Rabin-Karp 算法

目录

  • 1.概述
  • 2.代码实现
  • 3.应用

更多数据结构与算法的相关知识可以查看数据结构与算法这一专栏。

有关字符串模式匹配的其它算法:
【算法】Brute-Force 算法
【算法】KMP 算法

1.概述

(1)Rabin-Karp 算法是由 Richard M. Karp 和 Michael O. Rabin 于 1987 提出的字符串匹配算法,它的基本思想是利用哈希函数对字符串进行编码,通过比较哈希码来判断是否可能发生匹配,并在发生哈希碰撞时进一步验证字符串是否匹配

(2)Rabin-Karp 算法的步骤如下:

  • ① 计算模式字符串的哈希码。
  • ② 计算目标串中同样长度的窗口字符串的哈希码。
  • ③ 比较窗口字符串的哈希码和模式字符串的哈希码:
    • 如果哈希码相同,进行进一步比较确认是否匹配。
    • 如果哈希码不同,移动窗口到下一个位置,通过哈希码滑动更新窗口的哈希码。
  • ④ 重复步骤 ③,直到找到匹配或遍历完整个文本字符串。

Rabin-Karp 算法的关键点是哈希函数的选择和哈希码的比较。一个好的哈希函数应该能够实现高效计算和压缩成较小的哈希码,以提高算法的效率。而比较哈希码可能会产生哈希碰撞,即不同字符串具有相同的哈希码。当发生哈希碰撞时,需要通过进一步的比较来确定字符串是否匹配。

(3)在Rabin-Karp算法中,计算字符串的哈希值通常采用的方式是基于进制转换的方法。具体而言,我们可以使用多项式哈希 (polynomial hashing) 来计算字符串的哈希值。考虑一个字符串 s,其中包含 n 个字符,字符的 ASCII 值分别为 c1, c2, …, cn。我们选择一个适当的基数,通常是 ASCII 表的大小,这里假设为 256。我们还选择一个素数,通常是 Rabin-Karp 算法的参数,这里假设为 101。那么,字符串 s 的哈希值 H(s) 计算方式如下:

H ( s ) = ( ( c 1 × p ( n − 1 ) ) + ( c 2 × p ( n − 2 ) ) + . . . + ( c n × p 0 ) ) % M H(s) = ((c_1 × p^{(n-1)}) + (c_2 × p^{(n-2)}) + ... + (c_n × p^0)) ~ \% ~ M H(s)=((c1×p(n1))+(c2×p(n2))+...+(cn×p0)) % M

其中,p 表示进制数(这里是 256),n 表示字符串的长度,M 表示选择的素数(这里是 101)。例如,模式字符串 “ABAB” 的哈希值为 ((65 × 2563) + (66 × 2562) + (65 × 2561) + (66 × 2560)) % 101 = 13。

通过将字符的 ASCII 值乘以进制数的幂次方,并对结果进行求和,再对素数 M 取模,即可得到字符串的哈希值。这种计算方法具有较好的散列性质,可以在较小的哈希空间中尽可能地减少碰撞的发生。

(4)让我们通过一个例子来说明 Rabin-Karp 算法的工作过程。首先,假设我们有以下文本和模式字符串:文本串 “ABABABABC” 以及模式串 “ABAB”。此外,我们选择基数为 256,素数为 101 作为算法的参数。

  • 首先,我们计算模式字符串 “ABAB” 的哈希值为 13。
  • 接下来,我们计算文本中第一个与模式字符串等长的子串 “ABAB” 的哈希值。它的哈希值也为 13。
  • 由于哈希值相等,我们需要进行进一步的检查。我们比较模式字符串 “ABAB” 和文本中的子串 “ABAB” 字符串是否完全匹配。在此例中,它们是完全匹配的。
  • 如果我们没有找到匹配,我们需要通过滑动窗口的方式来更新子串的哈希值。在这种情况下,我们将滑动窗口向右移动一个位置,得到下一个子串 “BABA” 的哈希值。
  • 我们重复这些步骤,直到找到所有匹配的位置。

(5)上面需要注意的是,我们可以通过当前窗口字符串 “ABAB” 的哈希值来直接计算下一个窗口字符串 “BABA” 的哈希值,从而避免再次遍历 “BABA” 来计算其哈希值,提高了效率。我们以下图(来自《算法导论》)来说明:

在这里插入图片描述

为了方便说明,我们可以将字符串看作对应的数字字符串,再上图中,我们可以快速通过哈希值 31415 来计算下一个窗口的哈希值 14152(取模之后再计算也是一样的效果)。其主要思想在于 31415 的低 4 位与 14152 的高 4 位相同,并且我们知道 31415 的最高位与 14152 的最低位哦,因此只需要简单的数学计算便可以得到下一个窗口字符串的哈希值,从而保证在常数时间内更新下一个窗口字符串的哈希值。

2.代码实现

(1)Rabin-Karp 算法的代码实现如下:

class RabinKarpAlgorithm {//用于取哈希值时的除法因子private static final int PRIME = 101;//字符集的大小private static final int RADIX = 256;/** s: 目标串* t: 模式串* 返回值: 模式串在目标串中所有匹配成功的位置* */public List<Integer> rabinKarp(String s, String t) {List<Integer> indices = new ArrayList<>();int sLen = s.length();int tLen = t.length();int h = 1;for (int i = 0; i < tLen - 1; i++) {h = (h * RADIX) % PRIME;}//初始化窗口内字符串和模式串的哈希值int sHash = 0;int tHash = 0;for (int i = 0; i < tLen; i++) {sHash = (sHash * RADIX + s.charAt(i)) % PRIME;tHash = (tHash * RADIX + t.charAt(i)) % PRIME;}for (int i = 0; i <= sLen - tLen; i++) {//当前窗口中字符串的哈希码与模式串的哈希码相同if (sHash == tHash) {//检查窗口中字符串 s[i...i + tLen - 1] 是否与模式串 t 相等int j;for (j = 0; j < tLen; j++) {if (s.charAt(i + j) != t.charAt(j)) {break;}}if (j == tLen) {//匹配成功indices.add(i);}}if (i == sLen - tLen) {return indices;}//更新窗口中字符串的哈希值sHash = ((sHash - s.charAt(i) * h) * RADIX + s.charAt(i + tLen)) % PRIME;if (sHash < 0) {sHash += PRIME;}}return indices;}
}

(2)上述代码的测试如下:

class Test {public static void main(String[] args) {RabinKarpAlgorithm rabinKarpAlgorithm = new RabinKarpAlgorithm();String s = "cvbzxcvbnmacvb";String t = "cvb";List<Integer> indices = rabinKarpAlgorithm.rabinKarp(s, t);System.out.println(indices);}
}

输出结果如下:

[0, 5, 11]

3.应用

(1)Rabin-Karp 算法是一种常用的字符串匹配算法,适用于以下几个应用场景:

  • 字符串匹配:Rabin-Karp 算法可以用于在一个长文本串中高效地查找一个模式串的出现位置。它通过使用哈希函数和滚动哈希的技巧,在线性时间内完成匹配操作。
  • 模式匹配:Rabin-Karp 算法可用于检测一个文本串中是否包含指定的模式串。这在文本搜索、文件内容查找等应用中非常有用。
  • 字符串去重:Rabin-Karp 算法可以用于快速识别和去重重复的子串。通过计算子串的哈希值,并使用哈希表或哈希集合来记录已经出现的子串,可以高效地实现字符串去重的功能。
  • 数据校验:Rabin-Karp 算法可以用于快速计算数据块的哈希值,可以在数据传输或存储过程中用来验证数据的完整性。通过对比哈希值,可以判断数据是否被篡改或损坏。

(2)大家可以去 LeetCode 上找相关的字符串匹配的题目来练习,或者也可以直接查看 LeetCode 算法刷题目录 (Java) 这篇文章中的字符串章节。如果大家发现文章中的错误之处,可在评论区中指出。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务实战系列之MemCache

前言 书接前文&#xff0c;马不停蹄&#xff0c;博主继续书写Cache的传奇和精彩。 Redis主要用于数据的分布式缓存&#xff0c;通过设置缓存集群&#xff0c;实现数据的快速响应&#xff0c;同时也解决了缓存一致性的困扰。 EhCache主要用于数据的本地缓存&#xff0c;因无法保…

字符集与编码规则

字符集 强调&#xff1a;UTF-8是编码规则&#xff0c;不是字符集 过程&#xff1a; 字符 --查表获得对应数字&#xff0c;--编码 解码---查表----获取字符 ASCII码 &#xff1a;一个字节 8bit GBK字符集&#xff08;windows系统默认使用的GBK,系统显示ANSI&#xff09; 存…

JavaScript WebAPI(三)(详解)

这次介绍一下webAPI中的一些知识&#xff1a; 回调函数 回调函数是指 如果将函数A做为参数传递给函数B时&#xff0c;我们称函数A为回调函数 例如&#xff1a; // 立即执行函数中传递的函数是一个回调函数 (function(){ console.log("我是回调函数") })(); // …

人工智能时代:AIGC的横空出世

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;数据结构、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 什么是AIGC?二. AIGC的主要特征2.1 文本生成2.2 图像生成2.3 语音生成2.4 视…

基于若依的ruoyi-nbcio流程管理系统增加流程节点配置(三)

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 这一节主要是对每个流程节点的字段规则设置与操作规则设置&#xff0c;目前也是只针对自定义业务表单。 1、…

Android获取原始图片Bitmap的宽高大小尺寸,Kotlin

Android获取原始图片Bitmap的宽高大小尺寸&#xff0c;Kotlin val options BitmapFactory.Options()options.inJustDecodeBounds trueval decodeBmp BitmapFactory.decodeResource(resources, R.mipmap.p1, options)//此时&#xff0c;decode出来的decodeBmp宽高并不是原始图…

C语言-预处理与库

预处理、动态库、静态库 1. 声明与定义分离 一个源文件对应一个头文件 注意&#xff1a; 头文件名以 .h 作为后缀头文件名要与对应的原文件名 一致 例&#xff1a; 源文件&#xff1a;01_code.c #include <stdio.h> int num01 10; int num02 20; void add(int a, in…

OpenSSL 使用AES对文件加解密

AES&#xff08;Advanced Encryption Standard&#xff09;是一种对称加密算法&#xff0c;它是目前广泛使用的加密算法之一。AES算法是由美国国家标准与技术研究院&#xff08;NIST&#xff09;于2001年发布的&#xff0c;它取代了原先的DES&#xff08;Data Encryption Stand…

JVM GC算法

一, 垃圾回收分类: 按线程数分&#xff0c;可以分为串行垃圾回收器和并行垃圾回收器。 按工作模式分&#xff0c;可以分为并发垃圾回收器和独占式垃圾回收器 按碎片处理方式分&#xff0c;可以分为压缩式垃圾回收器和非压缩式垃圾回收器按工作的内存区间分&#xff0c;又可分为…

2000-2021年上市公司过度负债数据

2000-2021年上市公司过度负债数据 1、时间&#xff1a;2000-2021年 2、指标&#xff1a; 证券代码、证券简称、会计期间、上市日期、行业代码、行业名称、是否剔除ST或*ST股、是否剔除当年新上市、已经退市或被暂停退市的公司、产权性质、盈利能力、杠杆率行业中位数、成长性…

数据结构与算法-静态查找表

&#x1f31e; “清醒 自律 知进退&#xff01;” 查找 &#x1f388;1.查找的相关概念&#x1f388;2.静态查找表&#x1f52d;2.1静态查找表的类定义&#x1f52d;2.2顺序查找&#x1f52d;2.3二分查找&#x1f50e;二分查找例题 &#x1f52d;2.4分块查找&#x1f52d;2.5三…

oracle sql相关语法

SQL*PLUS 在SQL*PLUS执行&#xff0c;会在执行后显示查询的执行计划和统计信息 SET AUTOTRACE ON;SELECT * FROM your_table WHERE column_name value;SET AUTOTRACE OFF;PLSQL PLSQL查询sql界面&#xff0c;鼠标右键&#xff0c;点击执行计划&#xff0c;会出现sql的执行计…

鸿蒙原生应用/元服务开发-AGC分发如何生成密钥和和证书请求文件

HarmonyOS通过数字证书&#xff08;.cer文件&#xff09;和Profile文件&#xff08;.p7b文件&#xff09;等签名信息来保证应用的完整性&#xff0c;应用如需上架到华为应用市场必须通过签名校验。因此&#xff0c;开发者需要使用发布证书和Profile文件对应用进行签名后才能发布…

04_Flutter自定义Slider滑块

04_Flutter自定义Slider滑块 一.Slider控件基本用法 Column(mainAxisAlignment: MainAxisAlignment.start,children: <Widget>[Text("sliderValue: ${_sliderValue.toInt()}"),Slider(value: _sliderValue,min: 0,max: 100,divisions: 10,thumbColor: Colors.…

Java研学-配置文件

一 配置文件 1 作用–解决硬编码的问题 在实际开发中,有时将变量的值直接定义在.java源文件中;如果维护人员想要修改数据,无法完成(因为没有修改权限),这种操作称之为硬编码 2 执行原理: 将经常需要改变的数据定义在指定类型的文件中,通过java代码对指定的类型的文件进行操作…

软件测试框架实战:Python+Slenium搭建Web自动化测试框架全教程

PythonSelenium是一种流行的Web自动化测试框架&#xff0c;可以模拟真实的用户操作&#xff0c;对网页进行功能和样式的验证。要通过selenium测试网页&#xff0c;需要以下几个步骤&#xff1a; 安装selenium库和浏览器驱动 。使用selenium提供的方法来控制浏览器窗口大小、后…

【NeurIPS 2023】PromptIR: Prompting for All-in-One Blind Image Restoration

PromptIR: Prompting for All-in-One Blind Image Restoration&#xff0c; NeurIPS 2023 论文&#xff1a;https://arxiv.org/abs/2306.13090 代码&#xff1a;https://github.com/va1shn9v/promptir 解读&#xff1a;即插即用系列 | PromptIR&#xff1a;MBZUAI提出一种基…

非得让你会之MyBatis插件与Java动态代理

引言 咱们今天聊聊Java动态代理&#xff0c;这东西在开发中真的太常见了。比如Spring AOP、RPC&#xff0c;它们都离不开动态代理。然后&#xff0c;咱们再来说说MyBatis插件&#xff0c;这可是MyBatis框架中的一个超实用的功能&#xff0c;它就像是给MyBatis加了个“超能力”…

基于WebSocket实现客户聊天室

目录 一、实现聊天室原理 二、聊天室前端代码 三、聊天室后端代码&#xff08;重点&#xff09; 四、聊天室实现效果展示 一、实现聊天室原理 1.1 介绍websocket协议 websocket是一种通信协议&#xff0c;再通过websocket实现弹幕聊天室时候&#xff0c;实现原理是客户端首…

Unity Image - 镜像

1、为什么要使用镜像 在游戏开发过程中&#xff0c;我们经常会为了节省 美术图片资源大小&#xff0c;美术会将两边相同的图片进行切一半来处理。如下所示一个按钮 需要 400 * 236&#xff0c;然而美术只需要切一张 74*236的大小就可以了。这样一来图集就可以容纳更多的图片。…