【算法】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.mfbz.cn/a/210432.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

免费采集工具推荐,好文章值得收藏

采集工具的作用 在互联网的海洋中&#xff0c;有许多强大的免费采集工具&#xff0c;它们为用户提供了便捷、高效的方式&#xff0c;帮助用户从各种网站中收集、整理所需的信息。这些工具不仅广泛应用于市场研究、竞争情报等商业领域&#xff0c;同时也服务于学术研究、个人兴…

虚函数表和虚函数在内存中的位置

文章目录 结论验证 结论 虚函数表指针是虚函数表所在位置的地址。虚函数表指针属于对象实例。因而通过new出来的对象的虚函数表指针位于堆&#xff0c;声名对象的虚函数表指针位于栈 虚函数表位于只读数据段&#xff08;.rodata&#xff09;&#xff0c;即&#xff1a;C内存模…

量子测量-技术点杂录

目录: 高质量文章导航-持续更新中_GZVIMMY的博客-CSDN博客 前置:量子测量设备 电子显微镜:电子显微镜可以在非常高分辨率下观察生物组织、细胞和分子结构。通过调整电子束的强度和聚焦来观察细胞内部的微小结构。但是,电子显微镜需要对样品进行切片处理,而且在真空中进行…

配置中心--Spring Cloud Config

目录 概述 环境说明 步骤 创建远端git仓库 准备配置文件 配置中心--服务端 配置中心--客户端 配置中心的高可用 配置中心--服务端 配置中心--客户端 消息总线刷新配置 配置中心--服务端 配置中心--客户端 概述 因为微服务架构有很多个服务&#xff0c;手动一个一…

Xilinx FPGA平台DDR3设计详解(二):DDR SDRAM组成与工作过程

本文主要介绍一下DDR SDRAM的基本组成以及工作过程&#xff0c;方便大家更好的理解和掌握DDR的控制与读写。 一、DDR SDRAM的基本组成 1、SDRAM的基本单元 SDRAM的基本单元是一个CMOS晶体管和一个电容组成的电路。 晶体管最上面的一端&#xff0c;称作栅极&#xff0c;通过…

css实现简单的抽奖动画效果和旋转效果,还有春联效果

使用css的animation和transform和transition可以实现简单的图片放大缩小&#xff0c;旋转&#xff0c;位移的效果&#xff0c;由此可以延伸的动画效果还是挺多的&#xff0c;比如图片慢慢放大&#xff0c;图片慢慢旋转并放大&#xff0c;图片慢慢变化位置等等&#xff0c; 抽奖…

mall电商项目(学习记录2)

运行mall-admin Java项目 需要安装Redis&#xff0c;需要安装mysql&#xff0c;同时需要运行其项目提供的mall.sql 运行mall-admin后端程序 安装完Redis、mysql、HeidiSQL&#xff08;用于执行mall.sql&#xff0c;界面化操作高效直观&#xff09;、IntelliJ IDEA 运行mall-…

《算法通关村——原来滑动窗口如此简单》

《算法通关村——原来滑动窗口如此简单》 基本思想 滑动窗口的思想非常简单&#xff0c;如下图所示&#xff0c;假如窗口的大小是3&#xff0c;当不断有新数据来时&#xff0c;我们会维护一个大小为3的一个区间&#xff0c;超过3的就将新的放入老的移走。 这个过程有点像火车…

如何开发互联网医院系统源码?互联网医院小程序开发全流程解析

互联网医院系统源码的开发以及互联网医院小程序的设计是关键环节&#xff0c;本文将为您详细解析开发全流程。 一、需求分析与规划 第一步&#xff0c;明确系统的功能模块。同时&#xff0c;规划系统的整体架构、技术栈&#xff0c;在这里需要想到系统的可扩展性和性能。 二…

千梦网创:熟悉抖音内容创作的切入方式

因为身边抖音网红的资源比较近&#xff0c;所以虽然一直没有露脸去做短视频运营&#xff0c;但是最近也是跟随朋友一起开始了短视频的学习之路。 在参观过一些“超级直播间”之后&#xff0c;我们敲定了未来的两个盈利方向&#xff0c;这两个方向可以将我们身边的资源极致利用…

xxl-job 分布式任务调度框架

文章目录 分布式任务调度XXL-Job 简介XXL-Job 环境搭建XXL-Job (源码说明)配置部署调度中心docker安装 Bean模式任务(方法形式)-入门案例任务详解任务详解-执行器任务详解-基础配置任务详解-调度配置任务详解-基础配置任务详解-阻塞处理策略任务详解-路由策略 路由策略路由策略…

网络和Linux网络_8(传输层)TCP协议_续(流量控制+滑动窗口+拥塞控制+紧急指针+listen第二个参数)

目录 1. 流量控制 2. 滑动窗口 2.1 滑动窗口概念 2.2 滑动窗口模型详解 高速重发控制&#xff08;快重传&#xff09; 3. 拥塞控制和拥塞窗口 4. 延迟应答 5. 捎带应答 6. 面向字节流 7. 粘包问题 8. 16位紧急指针 9. listen的第二个参数 10. TCP总结异常情况与UD…

【上海大学数字逻辑实验报告】三、组合电路(二)

一、实验目的 掌握8421码到余3码的转换。掌握2421码到格雷码的转换。进一步熟悉组合电路的分析和设计方法。学会使用Quartus II设计8421码到余3码的转换电路逻辑图。学会使用Quartus II设计2421码到格雷码的转换电路逻辑图。 二、实验原理 8421码是最常用的BCD码&#xff0c…

权限的树形列表展示——基于APEX FancyTree Select

select distinct (o.PERMISSION_ID) as id, --数据ido.PARENT_PERMISSION_ID as PARENT_ID, --父ido.PERMISSION_NAME as title, --显示的标题o.PERMISSION_ID as VALUE, --标题对应的值1 as TYPE,casewhen (select cou…

图解系列--功能追加协议,构建Web内容

功能追加协议 1.消除 HTTP 瓶颈的 SPDY 1.1.HTTP 的瓶颈 使用 HTTP 协议探知服务器上是否有内容更新&#xff0c;就必须频繁地从客户端到服务器端进行确认。如果服务器上没有内容更新&#xff0c;那么就会产生徒劳的通信。 若想在现有 Web 实现所需的功能&#xff0c;以下这些…

国产Type-C接口逻辑协议芯片:Type-C显示器芯片方案

产品介绍 双Type-C盲插选型&#xff1a; LDR6282 PD3.0认证协议芯片&#xff0c;USB-IF TID号&#xff1a;212 支持iic&#xff0c;USB转UART&#xff0c;CC升级方式&#xff0c;多年市场验证&#xff0c;显示器市场出货量&#xff0c;显示器大厂采用兼容性NO.1。采用QFN32 5…

【全栈开发】使用NestJS、Angular和Prisma 打造全栈Typescript开发

在开发Angular应用程序时&#xff0c;我非常喜欢Typescript。使用NestJS&#xff0c;您可以以与Angular非常相似的方式编写后端。 我偶然发现了这个库&#xff0c;发现它非常有趣&#xff0c;所以我想设置一个简单的测试项目。一般来说&#xff0c;我主要使用SQL数据库&#x…

嵌入式 C 语言中的全局变量问题

大家好&#xff0c;今天分享一篇关于嵌入式C编程中全局变量问题的文章。希望对大家有所启发。 嵌入式特别是单片机os-less的程序&#xff0c;最易范的错误是全局变量满天飞。 这个现象在早期汇编转型过来的程序员以及初学者中常见&#xff0c;这帮家伙几乎把全局变量当作函数形…

Spring Data Redis切换底层Jedis 和 Lettuce实现

1 简介 Spring Data Redis是 Spring Data 系列的一部分&#xff0c;它提供了Spring应用程序对Redis的轻松配置和使用。它不仅提供了对Redis操作的高级抽象&#xff0c;还支持Jedis和Lettuce两种连接方式。 可通过简单的配置就能连接Redis&#xff0c;并且可以切换Jedis和Lett…

基于PLC的采摘机械手系统(论文+源码)

1.系统设计 本次设计围绕基于PLC的采摘机械手系统进行设计&#xff0c; PLC即可编程控制器其是一种常见的微处理器&#xff0c;本次拟采用西门子是S7-200 PLC&#xff0c;一方面对整个设计从器件选型到I/O分配&#xff0c;图纸绘制等进行设计&#xff0c;另一方面还通过组态王…