热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

字符串匹配RabinKarp算法讲解

问题描述:Rabin-Karp的预处理时间是O(m),匹配时间O((n-m1)m)既然与朴素算法的匹配时间一样,而且还多了一些预处理时间&

问题描述:

      Rabin-Karp的预处理时间是O(m),匹配时间O( ( n - m + 1 ) m )既然与朴素算法的匹配时间一样,而且还多了一些预处理时间,那为什么我们还要学习这个算法呢?虽然Rain-Karp在最坏的情况下与朴素匹配一样,但是实际应用中往往比朴素算法快很多而且该算法的期望匹配时间是O(n)【参照《算法导论》】,但是Rabin-Karp算法需要进行数值运算,速度必然不会比KMP算法快,那我们有了KMP算法以后为什么还要学习Rabin-Karp算法呢?个人认为学习的是一种思想,一种解题的思路,当我们见识的越多,眼界也就也开阔,面对实际问题的时候,就能找到更加合适的算法。比如二维模式匹配,Rabin-Karp就是一种好的选择。

      而且Rabin-Karp算法非常有趣,将字符当作数字来处理,基本思路:如果Tm是一个长度为 |P| 的T的子串,且转换为数值后模上一个数(一般为素数)与模式字符串P转换成数值后模上同一个数的值相同,则Tm可能是一个合法的匹配。

Rabin-Karp字符串匹配算法和前面介绍的《朴素字符串匹配算法》类似,也是对应每一个字符进行比较,不同的是Rabin-Karp采用了把字符进行预处理,也就是对每个字符进行对应进制数并取模运算,类似于通过某种函数计算其函数值,比较的是每个字符的函数值。预处理时间O(m),匹配时间是O((n-m+1)m)。Rabin-Karp算法的思想:假设待匹配字符串的长度为M,目标字符串的长度为N(N>M);
首先计算待匹配字符串的hash值,计算目标字符串前M个字符的hash值;
比较前面计算的两个hash值,比较次数N
-M+1:
若hash值不相等,则继续计算目标字符串的下一个长度为M的字符子串的hash值
若hash值相同,则需要使用朴素算法再次判断是否为相同的字串;

 

We can compute p in time O(m) using Horner's rule (see Section 32.1):

p
= P[m] + 10 (P[m - 1] + 10(P[m - 2] + . . . + 10(P[2] + 10P[1]) . . . )).
The value t0 can be similarly computed
from T[1 . . m] in time O(m).To compute the remaining values t1, t2, . . . , tn-m in time O(n - m), it suffices to observe that ts + 1 can be computed from ts in constant time, sincets + 1 = 10(ts - 10m - 1T[s + 1]) + T[s + m + 1].(34.1)
For example,
if m= 5 and ts = 31415, then we wish to remove the high-order digit T[s + 1] = 3 and bring in the new low-order digit (suppose it is T[s + 5 + 1] = 2) to obtaints+1 = 10(31415 - 10000.3) + 2= 14152 .

http://net.pku.edu.cn/~course/cs101/2007/resource/Intro2Algorithm/book6/chap34.htm

以上算法很简单,但是当模式字符串P的长度达到7以后就要出错了,即使将t,p定义为long unsigned int型也解决不了大问题,也就是说上面代码没什么用。

  其中b是基数,相当于把字符串看作b进制数。这样,字符串S=s1s2s3...sn从位置k+1开始长度为m的字符串子串S[k+1...k+m]的哈希值,就可以利用从位置k开始的字符串子串S[k...k+m-1]的哈希值,直接进行如下计算:H(S[k+1...k+m])=(H(S[k...k+m-1])* b - sk*b^m + s(k+m)) mod h

    该算法的难点就在于p和t的值可能很大,导致不能方便的对其进行处理。对这个问题有一个简单的补救办法,用一个合适的数q来计算p和t的模。每个字符其实十一个十进制的整数,所以p,t以及递归式都可以对模q进行,所以可以在O(m)的时间里计算出模q的p值,在O(n - m + 1)时间内计算出模q的所有t值。参见《算法导论》或http://net.pku.edu.cn/~course/cs101/2007/resource/Intro2Algorithm/book6/chap34.htm


 

递推式是如下这个式子: 

ts+1 = (d *( ts-T[s + 1]*h) + T[s + m + 1 ] ) mod q

例如,如果d = 10 (十进制)m= 5, ts = 31415,我们希望去掉最高位数字T[s + 1] = 3,再加入一个低位数字(假定 T[s+5+1] = 2)就得到:

ts+1 = 10*(31415 - 1000*3) +2 = 14152

于是,只要不断这样计算开始位置右移一位后的字符串子串哈希值,就可以在O(n)时间内得到所有位置对应的哈希值,从而可以在O(n+m)时间内完成字符串匹配。在实现时,可以用64位无符号整数计算哈希值,并取h等于2^64,通过自然溢出省去求模运算。

typedef unsigned long long ull;
const ull b=100000007;//哈希的基数;
//a是否在b中出现
bool contain(string C,string S)
{
int m&#61;C.length(),n&#61;S.length();if(m>n) return false;//计算b的m次方ull t&#61;1;for(int i&#61;0;ib;//计算C和S长度为m的前缀对应的哈希值ull Chash&#61;0,Shash&#61;0;for(int i&#61;0;iC[i];for(int i&#61;0;iS[i];//对S不断右移一位&#xff0c;更新哈希值并判断for(int i&#61;0;i&#43;m<&#61;n;i&#43;&#43;){if(Chash&#61;&#61;Shash) return true;//S从位置i开始长度为m的字符串子串等于C&#xff1b;if(i&#43;mm];}return false;
}

滚动哈希&#xff08;Rabin-Karp算法&#xff09;

 


 

hash( txt[s&#43;1 .. s&#43;m] ) &#61; ( d ( hash( txt[s .. s&#43;m-1]) – txt[s]*h ) &#43; txt[s &#43; m] ) mod q

hash( txt[s .. s&#43;m-1] ) : Hash value at shift s.
hash( txt[s&#43;1 .. s&#43;m] ) : Hash value at next shift (or shift s&#43;1)
d: Number of characters in the alphabet
q: A prime number
h: d^(m-1)

/* Following program is a C implementation of Rabin Karp
Algorithm given in the CLRS book
*/
#include

#include
<string.h>// d is the number of characters in the input alphabet
#define d 256/* pat -> patterntxt -> textq -> A prime number
*/
void search(char pat[], char txt[], int q)
{
int M &#61; strlen(pat);int N &#61; strlen(txt);int i, j;int p &#61; 0; // hash value for patternint t &#61; 0; // hash value for txtint h &#61; 1;// The value of h would be "pow(d, M-1)%q"for (i &#61; 0; i 1; i&#43;&#43;)h &#61; (h*d)%q;// Calculate the hash value of pattern and first// window of textfor (i &#61; 0; i ){p &#61; (d*p &#43; pat[i])%q;t &#61; (d*t &#43; txt[i])%q;}// Slide the pattern over text one by onefor (i &#61; 0; i <&#61; N - M; i&#43;&#43;){// Check the hash values of current window of text// and pattern. If the hash values match then only// check for characters on by oneif ( p &#61;&#61; t ){/* Check for characters one by one */for (j &#61; 0; j ){if (txt[i&#43;j] !&#61; pat[j])break;}// if p &#61;&#61; t and pat[0...M-1] &#61; txt[i, i&#43;1, ...i&#43;M-1]if (j &#61;&#61; M)printf("Pattern found at index %d \n", i);}// Calculate hash value for next window of text: Remove// leading digit, add trailing digitif ( i M ){t &#61; (d*(t - txt[i]*h) &#43; txt[i&#43;M])%q;// We might get negative value of t, converting it// to positiveif (t <0)t &#61; (t &#43; q);}}
}
/* Driver program to test above function */
int main()
{
char txt[] &#61; "GEEKS FOR GEEKS";char pat[] &#61; "GEEK";int q &#61; 101; // A prime number
search(pat, txt, q);return 0;
}

 

 

参考资料&#xff1a;http://www.geeksforgeeks.org/archives/11937

参考资料&#xff1a;http://net.pku.edu.cn/~course/cs101/2007/resource/Intro2Algorithm/book6/chap34.htm

http://www.cnblogs.com/feature/articles/1813967.html &#xff08;翻译PKU

转:https://www.cnblogs.com/Roni-i/p/9447409.html



推荐阅读
  • 本文详细介绍如何在SSM(Spring + Spring MVC + MyBatis)框架中实现分页功能。包括分页的基本概念、数据准备、前端分页栏的设计与实现、后端分页逻辑的编写以及最终的测试步骤。 ... [详细]
  • 我在尝试将组合框转换为具有自动完成功能时遇到了一个问题,即页面上的列表框也被转换成了自动完成下拉框,而不是保持原有的多选列表框形式。 ... [详细]
  • HDU 2537 键盘输入处理
    题目描述了一个名叫Pirates的男孩想要开发一款键盘输入软件,遇到了大小写字母判断的问题。本文提供了该问题的解决方案及实现方法。 ... [详细]
  • 本文探讨了如何使用Scrapy框架构建高效的数据采集系统,以及如何通过异步处理技术提升数据存储的效率。同时,文章还介绍了针对不同网站采用的不同采集策略。 ... [详细]
  • 本文详细探讨了编程中的命名空间与作用域概念,包括其定义、类型以及在不同上下文中的应用。 ... [详细]
  • 使用jQuery与百度地图API实现地址转经纬度功能
    本文详细介绍了如何利用jQuery和百度地图API将地址转换为经纬度,包括申请API密钥、页面构建及核心代码实现。 ... [详细]
  • 在AngularJS中,有时需要在表单内包含某些控件,但又不希望这些控件导致表单变为脏状态。例如,当用户对表单进行修改后,表单的$dirty属性将变为true,触发保存对话框。然而,对于一些导航或辅助功能控件,我们可能并不希望它们触发这种行为。 ... [详细]
  • 使用 ModelAttribute 实现页面数据自动填充
    本文介绍了如何利用 Spring MVC 中的 ModelAttribute 注解,在页面跳转后自动填充表单数据。主要探讨了两种实现方法及其背后的原理。 ... [详细]
  • 本文档提供了在Windows 10操作系统中安装Python 3及Scrapy框架的完整指南,包括必要的依赖库如wheel、lxml、pyOpenSSL、Twisted和pywin32的安装方法。 ... [详细]
  • 探索CNN的可视化技术
    神经网络的可视化在理论学习与实践应用中扮演着至关重要的角色。本文深入探讨了三种有效的CNN(卷积神经网络)可视化方法,旨在帮助读者更好地理解和优化模型。 ... [详细]
  • Docker基础入门与环境配置指南
    本文介绍了Docker——一款用Go语言编写的开源应用程序容器引擎。通过Docker,用户能够将应用及其依赖打包进容器内,实现高效、轻量级的虚拟化。容器之间采用沙箱机制,确保彼此隔离且资源消耗低。 ... [详细]
  • 在学习了Splay树的基本查找功能后,可能会觉得它与普通的二叉查找树没有太大的区别,仅仅是通过splay操作减少了时间开销。然而,Splay树之所以被誉为“序列之王”,主要在于其强大的区间操作能力。 ... [详细]
  • Hadoop MapReduce 实战案例:手机流量使用统计分析
    本文通过一个具体的Hadoop MapReduce案例,详细介绍了如何利用MapReduce框架来统计和分析手机用户的流量使用情况,包括上行和下行流量的计算以及总流量的汇总。 ... [详细]
  • Java连接MySQL数据库的方法及测试示例
    本文详细介绍了如何安装MySQL数据库,并通过Java编程语言实现与MySQL数据库的连接,包括环境搭建、数据库创建以及简单的查询操作。 ... [详细]
  • 本文总结了 #define 在 C/C++ 编程中的多种用途和技巧,包括定义常量、函数、宏以及条件编译等,并提供了详细的示例和注意事项。 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有