RK算法是对BF算法的进一步优化,很巧妙的使用了哈希算法,让匹配的效率有了很大的提升。

BF算法  这是关于BF暴力匹配算法的博客,大家可以先去看看。

RK算法的原理和实现

之前在讨论BF算法的时候,我们说过关于模式串长度m,和主串长度n,那么在主串中就会有n-m+1个长度为m的子串,我们只需要暴力的一一对比n-m+1个子串与模式串,就可以找出主串中与模式串匹配的子串。

但是这样就会出现一个问题,在每次检查子串和模式串是否匹配的时候,需要依次对比每个字符,比较耗时,所以我们现在就需要稍微进行改造,增加哈希算法,来加快子串与模式串的匹配。

具体的思路是通过哈希算法对主串中的n-m+1个子串分别求哈希值,然后与模式串的哈希值进行比较。如果某个子串的哈希值等于模式串,那就说明子串与模式串相匹配(暂时不讨论哈希冲突的问题)。因为哈希值是一个数字,数字之间比较是否相等是非常快速的,所以模式串和子串的比对的效率就提高了。

这时候大家可能也注意到一个问题了,就是尽管借助了哈希值,模式串与子串比对的效率提高了,不过通过哈希算法计算子串哈希值的过程,需要遍历子串中的每个字符,这个过程就比较耗时。也就是说,整体的效率并没有提高。

那我们如果假设主串和模式串对应的字符集只包含K个字符,我们可以用一个K进制数来表示一个子串,把K进制数转换为十进制数,作为子串的哈希值。现在看看这个例子。

例如:要处理的字符串只包含a到z这26个小写字母,那么我们就用26进制来表示一个子串,我们把a到z这26个字符映射到0到25这26个数字,如下图一样,计算哈希值时,相对于十进制字符串,我们只需要把进位从10改为26.

上面说的哈希算法有一个特点:在主串中,相邻两个子串哈希值的计算公式有一顶的关系。

上图这是十进制字符串和十六进制字符串的哈希值计算。

上图是相邻子串哈希值计算。

通过这个例子,我们可以得出这样的规律:相邻子串s[i-1]和s[i]对应的哈希值计算公式有交集,也就是说,我们可以使用s[i-1]的哈希值快速的计算出s[i]的哈希值。

不过,这里有一个小细节需要注意,那就是 26^(m-1) 这部分的计算,我们可以通过查表 的方法来提高效率。我们事先计算好 26^0、26^1、26^2……26^(m-1),并且存n储在一个 长度为 m 的数组中,公式中的“次方”就对应数组的下标。当我们需要计算 26 的 x 次方 的时候,就可以从数组的下标为 x 的位置取值,直接使用,省去了计算的时间。

RK算法的性能分析

RK算法耗时的逻辑主要包含两个部分:计算子串的哈希值和比较模式串与子串的哈希值。对于计算字串的哈希值,我们可以设计特殊的哈希算法,只需要扫描一遍主串就能得到所以子串的哈希值,因此,这一部分的时间复杂度为O(n)。对于比较模式串与子串的哈希值,这部分的时间复杂度也为O(n)。综合起来,RK算法的时间复杂度为O(n)。

这个时候可能还会出现一个问题,图个模式串很长,相对应的主串中的子串也很长,那么通过上面计算出的哈希值可能就会很大,如果哈希值超过了计算机整型类型可以表示的范围,那又应该怎么办呢?

实际上,前面设计的基于二十六进制的哈希算法是没有哈希冲突的,也就是说,一个字符串与一个二十六进制数一一对应,不同的字符串的哈希值肯定不一样,十六进制字符串对应的十进制也是不会有冲突的。不过,对于模式串很长的情况下,为了能将哈希值落在整形数据表示的范围内,存在哈希冲突也是可以接收的,那么此时又该怎么设计哈希算法呢?

哈希算法的设计方法有很多,我举一个例子说明一下。假设字符串中只包含 a~z 这 26 个 英文字母,那我们每个字母对应一个数字,比如 a 对应 1,b 对应 2,以此类推,z 对应 26。我们可以把字符串中每个字母对应的数字相加,最后得到的和作为哈希值。这种哈希 算法产生的哈希值的数据范围就相对要小很多了。
不过,你也应该发现,这种哈希算法的哈希冲突概率也是挺高的。当然,我只是举了一个最 简单的设计方法,还有很多更加优化的方法,比如将每一个字母从小到大对应一个素数,而 不是 1,2,3……这样的自然数,这样冲突的概率就会降低一些。那现在新的问题来了。之前我们只需要比较一下模式串和子串的哈希值,如果两个值相等,
那这个子串就一定可以匹配模式串。但是,当存在哈希冲突的时候,有可能存在这样的情 况,子串和模式串的哈希值虽然是相同的,但是两者本身并不匹配。
实际上,解决方法很简单。当我们发现一个子串的哈希值跟模式串的哈希值相等的时候,我 们只需要再对比一下子串和模式串本身就好了。当然,如果子串的哈希值与模式串的哈希值 不相等,那对应的子串和模式串肯定也是不匹配的,就不需要比对子串和模式串本身了。 所以,哈希算法的冲突概率要相对控制得低一些,如果存在大量冲突,就会导致 RK 算法的 时间复杂度退化,效率下降。极端情况下,如果存在大量的冲突,每次都要再对比子串和模 式串本身,那时间复杂度就会退化成 O(n*m)。但也不要太悲观,一般情况下,冲突不会很 多,RK 算法的效率还是比 BF 算法高的

基于C语言的代码

//
//  main.c
//  003--RK匹配算法
//
//  Created by CC老师 on 2019/10/29.
//  Copyright © 2019年 CC老师. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//d 表示进制
#define d 26//4.为了杜绝哈希冲突. 当前发现模式串和子串的HashValue 是一样的时候.还是需要二次确认2个字符串是否相等.
int isMatch(char *S, int i, char *P, int m)
{int is, ip;for(is=i, ip=0; is != m && ip != m; is++, ip++)if(S[is] != P[ip])return 0;return 1;
}//3.算出最d进制下的最高位
//d^(m-1)位的值;
int getMaxValue(int m){int h = 1;for(int i = 0;i < m - 1;i++){h = (h*d);}return h;
}/** 字符串匹配的RK算法* Author:Rabin & Karp* 若成功匹配返回主串中的偏移,否则返回-1*/
int RK(char *S, char *P)
{//1. n:主串长度, m:子串长度int m  = (int) strlen(P);int n  = (int) strlen(S);printf("主串长度为:%d,子串长度为:%d\n",n,m);//A.模式串的哈希值; St.主串分解子串的哈希值;unsigned int A   = 0;unsigned int St  = 0;//2.求得子串与主串中0~m字符串的哈希值[计算子串与主串0-m的哈希值]//循环[0,m)获取模式串A的HashValue以及主串第一个[0,m)的HashValue//此时主串:"abcaadddabceeffccdd" 它的[0,2)是ab//此时模式串:"cc"//cc = 2 * 26^1 + 2 *26 ^0 = 52+2 = 54;//ab = 0 * 26^1 + 1 *26^0 = 0+1 = 1;for(int i = 0; i != m; i++){//第一次 A = 0*26+2;//第二次 A = 2*26+2;A = (d*A + (P[i] - 'a'));//第一次 st = 0*26+0//第二次 st = 0*26+1St = (d*St + (S[i] - 'a'));}//3. 获取d^m-1值(因为经常要用d^m-1进制值)int hValue = getMaxValue(m);//4.遍历[0,n-m], 判断模式串HashValue A是否和其他子串的HashValue 一致.//不一致则继续求得下一个HashValue//如果一致则进行二次确认判断,2个字符串是否真正相等.反正哈希值冲突导致错误//注意细节://① 在进入循环时,就已经得到子串的哈希值以及主串的[0,m)的哈希值,可以直接进行第一轮比较;//② 哈希值相等后,再次用字符串进行比较.防止哈希值冲突;//③ 如果不相等,利用在循环之前已经计算好的st[0] 来计算后面的st[1];//④ 在对比过程,并不是一次性把所有的主串子串都求解好Hash值. 而是是借助s[i]来求解s[i+1] . 简单说就是一边比较哈希值,一边计算哈希值;for(int i = 0; i <= n-m; i++){if(A == St)if(isMatch(S,i,P,m))return i;St = ((St - hValue*(S[i]-'a'))*d + (S[i+m]-'a'));}return -1;
}int main()
{char *buf="abcaadddabceeffccdd";char *ptrn="cc";printf("主串为%s\n",buf);printf("子串为%s\n",ptrn);int index = RK(buf, ptrn);printf("find index : %d\n",index);return 1;
}

一看就懂的字符串匹配算法 之 RK算法相关推荐

  1. 一看就懂的字符串匹配算法 之 BM算法

    先来一个导读,关于BM算法开始有了难度,大家一定要静下心,咬着呀也得给我读下去,这样你才能有收获. 我们在文本编辑器中,我们经常用到查找及替换功能.比如说,在Word文件中,通过查找及替换功能,可以把 ...

  2. diff算法阮一峰_【重学数据结构与算法(JS)】字符串匹配算法(三)——BM算法

    前言 文章的一开头,还是要强调下字符串匹配的思路 将模式串和主串进行比较 从前往后比较 从后往前比较 2. 匹配时,比较主串和模式串的下一个位置 3. 失配时, 在模式串中寻找一个合适的位置 如果找到 ...

  3. 大量的数据做字符串匹配_【重学数据结构与算法(JS)】字符串匹配算法(三)——BM算法...

    前言 文章的一开头,还是要强调下字符串匹配的思路 将模式串和主串进行比较 从前往后比较 从后往前比较 2. 匹配时,比较主串和模式串的下一个位置 3. 失配时, 在模式串中寻找一个合适的位置 如果找到 ...

  4. 字符串匹配算法之BM算法

    BM算法,全称是Boyer-Moore算法,1977年,德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明了一种新的字符串匹配算法. BM算法定义了两个规则: ...

  5. 字符串匹配算法:Horspool算法

    Horspool 字符串匹配算法对Boyer-Moore算法的简化算法. Horspool 算法是一种基于后缀匹配的方法,是一种"跳跃式"匹配算法,具有sub-linear亚线性时 ...

  6. 字符串匹配算法:Sunday算法

    背景 我们第一次接触字符串匹配,想到的肯定是直接用2个循环来遍历,这样代码虽然简单,但时间复杂度却是\(Ω(m*n)\),也就是达到了字符串匹配效率的下限.于是后来人经过研究,构造出了著名的KMP算法 ...

  7. 字符串处理 —— 单模式匹配 —— 朴素的字符串匹配算法(BF 算法)

    [算法流程] 朴素的字符串匹配算法即暴力匹配算法(BF,Brute Force),其本质是暴力枚举,主要特点有: 没有预处理阶段: 滑动窗口总是后移 1 位: 对模式中的字符的比较顺序不限定,可以从前 ...

  8. 字符串匹配算法之Sunday算法

    字符串匹配查找算法中,最着名的两个是KMP算法(Knuth-Morris-Pratt)和BM算法(Boyer-Moore).两个算法在最坏情况下均具有线性的查找时间.但是在实用上,KMP算法并不比最简 ...

  9. 字符串匹配算法(BF算法KMP算法)

    字符串匹配算法 暴力匹配(BF)算法 KMP算法 next数组 求next数组的练习 next数组的优化(nextval数组) 练习 暴力匹配(BF)算法 BF算法,即暴力(Brute Force)算 ...

最新文章

  1. 【开源方案共享】VDO-SLAM:基于视觉的动态SLAM感知系统
  2. 伪共享(false sharing),并发编程无声的性能杀手
  3. DL之ResNet:ResNet算法的简介(论文介绍)、架构详解、案例应用等配图集合之详细攻略
  4. 学习笔记27—python中numpy.ravel() 和 flatten()函数
  5. 在ie8下ext显示的问题
  6. 注解@Component方式代替xml装配bean
  7. r语言系统计算上是奇异的_R语言实现并行计算
  8. 【数据结构学习之完全从零实现所有数据结构的代码编写之二】智能指针
  9. mysql 撤销删除_线上磁盘告警,mysql无法释放空间,踩了个大坑,大家记得别踩坑...
  10. springboot整合netty
  11. VB利用SHFileOperation实现拷贝、删除、重命名文件
  12. 常见的十二种逻辑谬误
  13. 计算机专业英语四六级,英语四级历年真题,历年四六级真题电子版?
  14. token与refresh token
  15. 年利率、七日年化收益率、万份收益
  16. MSP430初学:MSP430单片机C语言基础(二)
  17. JAVA计算机毕业设计网课系统(附源码、数据库)
  18. linux du | sort 命令查找磁盘占用大户
  19. web前端 - Vue常见问题汇总及解决方案
  20. ssd nvme sata_NVMe SSD与传统SATA SSD

热门文章

  1. 八大排序(Java完整版)
  2. 关于Instagram广告如何运作
  3. 使用 Python 分析《我不是药神》豆瓣电影短评
  4. TG纸飞机频道创建和维护
  5. ARM售价超400亿美元?软银与英伟达有望达成史上最大半导体交易
  6. 5.1配置IBGP和EBGP
  7. 智能电表远红外远程抄表实施技术方案
  8. 员工信息管理系统(JavaWeb结课项目)
  9. numpy 分母为零的处理办法
  10. html页面获取宽和高