RobinKarp(字符串哈希)---分析与实现(C++)

news2024/11/25 21:22:32

1. 简述

给定字符串pattern和串text。求串pattern在串text中出现的位置。

暴力比较是逐个字符比较来确定两个串是否相等,若当前比较失败

则回到开始字符对应字符的后一个字符重复过程。

在这里插入图片描述

哈希就是一个大范围到小范围的映射

字符串哈希则是通过比较两个串的哈希值相等来判断两个字符串是

否相等,如果每次都要像暴力字符匹配那样重新计算哈希值的话,

那么复杂都就太高了。所以这里用到了一种技巧:滚动哈希。

在这里插入图片描述

2. 滚动哈希

由上面的图可以知道,要让字符串哈希求值快,则需要

Hash(0, p.size)Hash(1, p.size + 1)的转换快。

所以我们这里直接自然想到了,进制的表示。

所以我们很自然的定义哈希函数

H ( s t r ) = a 0 s t r [ 0 ] + a 1 s t r [ 1 ] . . . + a s t r . s i z e ( ) s t r [ s t r . s i z e ( ) − 1 ] H(str) = a^0str[0] + a^{1}str[1]...+a^{str.size()}str[str.size() - 1] H(str)=a0str[0]+a1str[1]...+astr.size()str[str.size()1]

H ( 0 , p a t . s i z e ( ) − 1 ) = H ( s t r ) H ( 1 , p a t . s i z e ( ) − 1 ) = H ( s t r ) − s t r [ 0 ] H(0, pat.size() - 1) = H(str) H(1, pat.size() - 1) = H(str) - str[0] H(0,pat.size()1)=H(str)H(1,pat.size()1)=H(str)str[0]
推导式
H ( 1 , s t r . s i z e ( ) ) = ( H ( s t r ) − s t r [ 0 ] ) / b a s e + a p a t . s i z e ( ) − 1 ∗ s t r [ s t r . s i z e ( ) ] H(1, str.size()) = (H(str) - str[0])/base + a^{pat.size() - 1}*str[ {str.size()}] H(1,str.size())=(H(str)str[0])/base+apat.size()1str[str.size()]

3. 更进一步

如果直接这样运算的话,H(str)的值会随着字符串长度的增加而呈指数级的增长。我

们希望值落在一个区间,所以通常会模上一个数使得哈希值在给定区间。

a % p = c < = > a = k p + c ( 0 < c < p ) a \% p = c <=> a = kp + c ( 0 < c < p) a%p=c<=>a=kp+c(0<c<p)

因为我们无法从

( ∑ i = 1 p . s i z e − 1 b a s e i t x t [ i ] ) % M O D ({\sum_{i=1}\limits^{p.size - 1}} {{base}^{i}txt[i]})\% MOD (i=1p.size1baseitxt[i])%MOD

推出
( ∑ i = 1 p . s i z e − 1 b a s e i − 1 t x t [ i ] ) % M O D ({\sum_{i = 1}\limits^{p.size - 1}{base^{i - 1}txt[i]}}) \% MOD (i=1p.size1basei1txt[i])%MOD
因为
a ∗ b % M O D = ( a % M O D ) ∗ ( b % M O D ) % M O D a * b \% MOD = (a \% MOD) * (b \% MOD) \% MOD ab%MOD=(a%MOD)(b%MOD)%MOD
成立,而

a / b % M O D ≠ ( a % M O D ) / ( b % M O D ) % M O D a/b \% MOD \neq (a\%MOD)/(b\%MOD)\%MOD a/b%MOD=(a%MOD)/(b%MOD)%MOD

所以原来的哈希函数不行,而我们反着来的时候就可以了。

H ( 0 , p . s i z e − 1 ) = ∑ i = 0 p . s i z e − 1 b a s e p . s i z e − 1 − i ∗ s t r [ i ] H(0,p.size - 1) = \sum_{i = 0}\limits^{ p.size - 1} base^{p.size - 1 - i} * str[i] H(0,p.size1)=i=0p.size1basep.size1istr[i]
此时的H(0, p.size - 1)H(1, p.size)推导式为

H ( 1 , p . s i z e ) = { ( H ( 0 , p . s i z e − 1 ) − ( s t r [ 0 ] b a s e p . s i z e − 1 % M O D ) + M O D ) % M O D ∗ b a s e + s t r [ p . s i z e ] } % M O D H(1, p.size) = \{(H(0, p.size - 1) - (str[0]base^{p.size - 1}\%MOD) + MOD)\%MOD*base + str[p.size]\} \%MOD H(1,p.size)={(H(0,p.size1)(str[0]basep.size1%MOD)+MOD)%MODbase+str[p.size]}%MOD
所以我们应该先求出 b a s e p . s i z e − 1 % M O D base^{p.size - 1} \% MOD basep.size1%MOD

求模运算时要特别注意是否有相减为负数的情况。

3. 实现


#include <iostream>

#include <boost/thread.hpp>
#include <boost/asio.hpp>

#include <string>

int robinKarp(const char *pat, const char *txt, uint8_t base)
{
    if (!pat || !txt || !base)
        return -1;

    int pLen = (int) strlen(pat);
    int tLen = (int) strlen(txt);
    int MOD = 251;

    if (pLen > tLen)
        return -1;

    int patHashVal = 0;

    int mem_base = 1;
    int cTxtHashVal = 0;


    for ( int i = 0; i < pLen; ++i) {
        cTxtHashVal += mem_base * txt[ pLen - 1 - i ] ;
        patHashVal += mem_base * pat[ pLen - 1 - i ];
        cTxtHashVal %= MOD;
        patHashVal %= MOD;

        if ( i + 1 != pLen)
            mem_base = (mem_base * base) % MOD;
    }
    if (cTxtHashVal == patHashVal)
        return 0;

    for ( int i = pLen;i < tLen; ++i) {



        cTxtHashVal = (cTxtHashVal - (mem_base * txt[i - pLen])%MOD + MOD)%MOD;
        cTxtHashVal = (cTxtHashVal * base + txt[i]) % MOD;

        if (cTxtHashVal == patHashVal) {
            //printf("%d\t", i - pLen + 1);
             return i - pLen + 1;
        }
    }

    return -1;
}

int main( int argc, char **argv)
{

    std::string txt("abeaabcabc");
    std::string pat("abc");


    int ret = robinKarp(pat.c_str(), txt.c_str(), 255);

    if ( -1 != ret ) {
        std::cout << "match pos: " << ret << std::endl;
    }

    return 0;
}

4. ref

brilliant
geekforgeeks

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

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

相关文章

Leetcode 剑指 Offer II 030. 插入、删除和随机访问都是 O(1) 的容器

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 设计一个支持在平均 时间复杂度 O(1) 下&#xff0c;执行以下操作…

使用NLPAUG 进行文本数据的扩充增强

在机器学习中&#xff0c;训练数据集的质量在很大程度上决定了模型的有效性。我们往往没有足够的多样化数据&#xff0c;这影响了模型的准确性。这时数据增强技术就派上了用场。 数据增强可以通过添加对现有数据进行略微修改的副本或从现有数据中新创建的合成数据来增加数据量…

代码随想录训练营Day53|1143.最长公共子序列;1035.不相交的栈;53.最大子序和

1143.最长公共子序列 class Solution {public int longestCommonSubsequence(String text1, String text2) {int[][] dp new int[text1.length()1][text2.length()1];for(int i1;i<text1.length();i){for(int j1;j<text2.length();j){if(text1.charAt(i-1)text2.charAt(…

12性能提升:如何提升gRPC系统性能

这篇文章我们来一起学习下如何提升gRPC系统服务的性能。 gRPC 是一个高性能、开源的 RPC 框架,设计目标是支持多种编程语言和多种平台。它基于 Google 发布的 Protobuf(Protocol Buffers)序列化协议,可以在不同的应用程序之间传输数据。gRPC 具有高效率和可扩展性的特点,…

python3 爬虫相关学习7:使用 BeautifulSoup下载网页图片到本地文件夹

目录 1 一个爬图片pic的代码的例子 1.1 学习的原文章 1.2 原始代码的问题总结 问题1 问题2 问题3 其他问题 1.3 原始代码 2 直接在cmd里 python运行报错 和 处理 2.1 运行报错 2.2 报错原因&#xff1a; 没有提前安装这个bs4 模块 2.3 如何提前知道我的python环境…

【微信小程序】wxml、wxss、js、json文件介绍

&#x1f609;博主&#xff1a;初映CY的前说(前端领域) ,&#x1f4d2;本文核心&#xff1a;微信小程序的入门介绍 【前言】书接上回&#xff0c;我们知道了一个小程序的构成结构&#xff0c;接下来我们来进一步学习小程序的目录结构中的.wxml、.wxss、.js、.json。 目录 ⭐ 一…

一学就会-----链表中倒数第K个节点

文章目录 题目描述思路一代码示例思路二代码示例 题目描述 输入一个链表&#xff0c;输出该链表中倒数第k个结点。 图片示例&#xff1a; 思路一 由于这道题目并没有要求时间复杂度&#xff0c;我们完全可以先遍历一遍链表&#xff0c;得到链表的结点总数&#xff08;count&am…

利用Zookeeper实现集群选举

什么是Zookeeper 分布式开源协调系统&#xff0c;数据模型简单&#xff0c;可以实现同步&#xff0c;配置管理&#xff0c;分组管理&#xff0c;分命名空间管理等。 技术本质 一个原子消息传递系统&#xff0c;它使所有服务器保持同步 FLP(3个科学家名字命名) 理论角度&…

【Spring Security】的RememberMe功能流程与源码详解,基础-进阶-升级-扩展,你学会了吗?

文章目录 前言原理 基础版搭建初始化sql依赖引入配置类验证 源码分析 进阶版集成源码分析疑问1疑问2 鉴权 升级版集成初始化sql配置类验证 源码分析鉴权流程 扩展版 前言 之前我已经写过好几篇权限认证相关的文章了&#xff0c;有想复习的同学可以查看【身份权限认证合集】。今…

OpenAI官方提示词课(三)如何总结文章

现在是信息爆炸时代&#xff0c;打开手机&#xff0c;各种文章扑面而来。我们的精力是有限的。如果有人帮忙把文章总结好给我们&#xff0c;这不就节省了很多时间嘛&#xff01;我们也就可以阅读更多的文章了。 恰好大语言模型在总结文章方面非常有天赋。 下面来看看示例。 …

数学基础第二天

介绍 对于Hissian矩阵是正定的&#xff0c;在这一点是整个范围内的最小值&#xff0c;y在各个方向的二阶导数都是>0的 对于Hissian矩阵是负定的&#xff0c;在这一点是整个范围内的最大值&#xff0c;y在各个方向的二阶导数都是<0的, 对于Hissian矩阵是不定的&#xff…

有了这个工具,支付宝商家多个账号下的账单管理更方便了

大家好&#xff0c;我是小悟 为方便拥有多个支付宝账号的商家获取自身业务、资金数据及下载对账单的能力&#xff0c;为商家提供了商家账单产品&#xff0c;商家可以通过该产品系统化接入账单数据&#xff0c;实现支付宝商家多个账号账单管理的功能。 为拥有多个支付宝账号的…

华为OD机试真题 JavaScript 实现【求符合要求的结对方式】【2023Q1 100分】,附详细解题思路

一、题目描述 用一个数组A代表程序员的工作能力&#xff0c;公司想通过结对编程的方式提高员工的能力&#xff0c;假设结对后的能力为两个员工的能力之和&#xff0c;求一共有多少种结对方式使结对后能力为N。 二、输入描述 6 2 3 3 4 5 1 6 第一行为员工的总人数&#xff…

centos 7 安装git并配置ssh

一、安装 1、查看是否安装git <span style"color:#333333"><span style"background-color:#ffffff"><code class"language-perl">rpm -qa|<span style"color:#0000ff">grep</span> git </code>…

【白嫖系列】永久免费域名申请教程 eu.org

&#x1f951; Welcome to Aedream同学 s blog! &#x1f951; 文章目录 eu.org注册激活注册域名解析 eu.org eu.org 一个从1996开始提供免费域名的组织, 其官网地址是 https://nic.eu.org/ 他帮助学生、爱好者或者非营利组织不用花费购买域名就可能拥有自己的免费域名&#x…

2023.6.9小记——ARM的工作模式与状态

今天打算学一点就写一点&#xff0c;不然全部堆积到晚上压力太大了&#xff0c;有些东西写不完就要睡觉了&#x1f4a4; 1. 什么是numpy&#xff1f; 1.1 numpy简介 是Python中的用于科学计算的库&#xff0c;提供高性能的多维数组对象和对应的操作函数&#xff0c;用于处理大…

微信小程序——实现蓝牙设备搜索及连接功能

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

NewBing神器,让你在Chrome轻松使用ChatGPT4智能AI

ChatGPT4 相对于 3.5 优势 相较于 ChatGPT3.5&#xff0c;ChatGPT4 有以下优势&#xff1a; 更大的模型规模&#xff1a;ChatGPT4 有超过 16 亿个参数&#xff0c;是 ChatGPT3.5 的 4 倍之多&#xff0c;这意味着它可以处理更复杂的对话场景和更长的对话历史。更好的对话质量…

通过xfsdump和xfsrestore命令实现RHEL7 xfs文件系统误删除文件的恢复

在linux系统中&#xff0c;我们有时会“不小心”误删除一些文件&#xff0c;如果是自己是测试环境服务器可能“无所谓”。但是一旦发生在客户的生产环境&#xff0c;那就是“重大安全事故”。 我们能不能提前对一些重要的文件系统进行备份&#xff0c;以便当我们真的误删除一些…

2023最新最全面Java复习路线(含P5-P8),已收录 GitHub

小编整理出一篇 Java 进阶架构师之路的核心知识&#xff0c;同时也是面试时面试官必问的知识点&#xff0c;篇章也是包括了很多知识点&#xff0c;其中包括了有基础知识、Java 集合、JVM、多线程并发、spring 原理、微服务、Netty 与 RPC 、Kafka、日记、设计模式、Java 算法、…