字符串匹配——RabinKarp算法

给定主串T和模式串P,返回P在T中首次出现的位置,如果P不存在于T中,返回-1。

这样的问题就是字符串匹配问题,这里给出RabinKarp算法的思想。

设主串T的长度为n,模式串P的长度为m。

主串匹配起始位置s从0到n-m,计算出T[s..s+m-1]的对应值,与P[0…m-1]的对应值进行比较,如果相同,则匹配成功。不同,则s右移一位,也就是计算出T[s+1…s+m]的对应值。

对应值的计算方法可以称作简单的指纹算法。

RabinKarp算法中的指纹算法是基于hash函数的。

其原理为 h = f % q,假设q=5,hash(12)= 12 % 5 = 2

hash函数的性质:

  • if hash(s1) ≠ hash(s2) then s1 ≠ s2
  • 但是hash(s1) = hash(s2)不意味着s1 = s2

模运算的性质:

  • (a + b) % q = (a % q + b % q) % q
  • (a * b) % q = ((a % q) * (b % q)) % q

假设字母表大小为d,hash因子为q,主串为T,长度为n,当前起始位置为s,模式串为P,长度为m,fp为P的指纹值,ft为T当前的指纹值。

预处理:

  • fp = (P[m - 1] + d * (P[m - 2] + d * (P[m - 3] + … + d * ((P[1] + d * (P[0] % q)) % q)…) % q
  • ft = (T[m - 1] + d * (T[m - 2] + d * (T[m - 3] + … + d * ((T[1] + d * (T[0] % q)) % q)…) % q

ft的迭代过程(右移一位):

  • ft = (ft - T[s] * 10m−110^{m - 1}) * 10 + T[s + m]) % q

伪代码

假设字母表大小为d

RabinKarp(T, P)
01 q <- a prime larger than m or less than the  machine word length / d
02 c <- d^(m-1) mod q // run a loop multiplying by d mod q
03 ft <- 0; fp <- 0
04 for i <- 0 to m-1 // preprocessing
05  ft <- (d * ft + T[i]) mod q
06  fp <- (d * fp + P[i]) mod q
07 for s <- 0 to n - m // matching
08  if fp = ft then // run a loop to compare strings
09      if P[0...m-1] = T[s...s+m-1] return s
10  else ft <- ((ft - T[s]*c)*d + T[s+m]) mod q
11 return -1

实现代码

生成字母表

// 字母表
int alphaBet[maxNum];
// 字母表字母个数,也就是d进制
int d;
// 生成字母表,返回字母表字母个数
int markAlphaBet(string T, string P) {// 初始化字母表memset(alphaBet, 0, sizeof(alphaBet));int d = 0;for(int i = 0; i < T.length(); i++) {if(!alphaBet[T[i]]) {alphaBet[T[i]] = d++;}}for(int i = 0; i < P.length(); i++) {if(!alphaBet[P[i]]) {alphaBet[P[i]] = d++;}}return d;
}

RabinKarp算法

const int prime = 9999991;
int rabinKarp(string T, string P) {// 字符串长度int n = T.length();int m = P.length();// 计算d^(m-1) mod qint c = 1;for(int i = 0; i < m - 1; i++) {c = (d * c) % prime;}// 初始化int ft = 0;int fp = 0;// 预处理,计算T[0...m-1]和P[0...m-1]的hash值for(int i = 0; i < m; i++) {ft = (d * ft + alphaBet[T[i]]) % prime;fp = (d * fp + alphaBet[P[i]]) % prime;}// 匹配for(int i = 0; i <= n - m; i++) {// hash值相同if(ft == fp) {// 判断字符串T[i...i+m-1]与P[0...m-1]是否相等for(int j = 0; j < m; j++) {if(T[i + j] != P[j]) {break;}if(j == m - 1) {return i;}}} else {// 计算T[i+1...i+m]的hash值ft = ((ft - alphaBet[T[i]] * c) * d + alphaBet[T[i + m]]) % prime;}}return -1;
}

测试主程序

#include <iostream>
#include <cstring>using namespace std;const int maxNum = 1000 + 5;// 字母表
int alphaBet[maxNum];
// 字母表字母个数,也就是d进制
int d;
// 生成字母表,返回字母表字母个数
int markAlphaBet(string T, string P) {// 初始化字母表memset(alphaBet, 0, sizeof(alphaBet));int d = 0;for(int i = 0; i < T.length(); i++) {if(!alphaBet[T[i]]) {alphaBet[T[i]] = d++;}}for(int i = 0; i < P.length(); i++) {if(!alphaBet[P[i]]) {alphaBet[P[i]] = d++;}}return d;
}const int prime = 9999991;
int rabinKarp(string T, string P) {// 字符串长度int n = T.length();int m = P.length();// 计算d^(m-1) mod qint c = 1;for(int i = 0; i < m - 1; i++) {c = (d * c) % prime;}// 初始化int ft = 0;int fp = 0;// 预处理,计算T[0...m-1]和P[0...m-1]的hash值for(int i = 0; i < m; i++) {ft = (d * ft + alphaBet[T[i]]) % prime;fp = (d * fp + alphaBet[P[i]]) % prime;}// 匹配for(int i = 0; i <= n - m; i++) {// hash值相同if(ft == fp) {// 判断字符串T[i...i+m-1]与P[0...m-1]是否相等for(int j = 0; j < m; j++) {if(T[i + j] != P[j]) {break;}if(j == m - 1) {return i;}}} else {// 计算T[i+1...i+m]的hash值ft = ((ft - alphaBet[T[i]] * c) * d + alphaBet[T[i + m]]) % prime;}}return -1;
}/**
IN
at the thought of
thoughOUT
7
**/
int main() {// 主串和模式串string T, P;while(true) {// 获取一行getline(cin, T);getline(cin, P);d = markAlphaBet(T, P);int res = rabinKarp(T, P);if(res == -1) {cout << "主串和模式串不匹配。" << endl;} else {cout << "模式串在主串的位置为:" << res << endl;}}return 0;
}

输出数据

at the thought of
though
模式串在主串的位置为:7
abcdefg
gfedcba
主串和模式串不匹配。
fgdajhkfhjaskdlfgbyue
yue
模式串在主串的位置为:18
frajlkfajsdlkfgjkljkleg
jsd
模式串在主串的位置为:8
aaaaaaaaaaaaaaa
a
模式串在主串的位置为:0

算法分析

  • if q 是素数, hash函数将会使m位字符串在q个值中均匀分布

    • 因此,仅有s个轮换中的每第q次才需要匹配指纹(匹配需要比较O(m) 次)
  • 期望运行时间(如果q > m):
    • 生成字母表:O(n + m)
    • 预处理:O(m)
    • 外循环:O(n - m)
    • 所有内循环:n−mqm=O(n−m)\frac{n-m}{q}m = O(n - m)
    • 总时间:O(n + m)
  • 最坏运行时间:O(nm)

字符串匹配——RabinKarp算法相关推荐

  1. 字符串匹配 - RK算法

    名称来由 RK 算法的全称叫 Rabin-Karp 算法. 它是由两位发明者 Rabin 和 Karp 的名字来命名的算法. 实现思路 BF算法的实现思路是对主串n中的每一个连续子串n1,都与模式串m ...

  2. 字符串匹配 RK 算法总结

    背景 RK算法是通过比较字符串的哈希值来实现字符串匹配,其核心思想是把字符串转换为 N 进制的数字. 本文主要是学习和记录下其实现思想,在日常项目中字符串查找使用 string 的 find 函数即可 ...

  3. 字符串匹配--Sunday算法 1

    字符串匹配(查找)算法是一类重要的字符串算法(String Algorithm).有两个字符串, 长度为m的haystack(查找串)和长度为n的needle(模式串), 它们构造自同一个有限的字母表 ...

  4. kmp算法详解php,php中字符串匹配KMP算法实现例子

    KMP算法是一个比较高级的算法了,加了改进了,下面我们来在php中实现KMP算法,希望例子对各位同学会带来帮助哦. kmp算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J. ...

  5. 字符串匹配——BMH算法

    字符串匹配--BMH算法 给定主串T和模式串P,返回P在T中首次出现的位置,如果P不存在于T中,返回-1. 这样的问题就是字符串匹配问题,这里给出BMH算法的思想. 设主串T的长度为n,模式串P的长度 ...

  6. 【超详细图解】字符串匹配Boyer-Moore算法:文本编辑器中的查找功能是如何实现的?

    关于字符串匹配算法有很多,之前我有讲过一篇 KMP 匹配算法:图解字符串匹配 KMP 算法,不懂 kmp 的建议看下,写的还不错,这个算法虽然很牛逼,但在实际中用的并不是特别多.至于选择哪一种字符串匹 ...

  7. 两个字符串匹配度算法

    在工作过程中,需要用到两个字符串匹配度算法,网上参考一些资料,写了一个匹配度算法类,项目中用到了而且效果很不错,今天给大家分享. 可以直接复制到你的项目中,就一个调用函数,非常简单. public c ...

  8. C++实现字符串匹配KMP算法

    文章目录 1. 概述 2. 代码实现 3. 代码测试 1. 概述 Kmp算法的介绍及思想参阅下面两篇文章: 字符串匹配KMP算法 算法)通俗易懂的字符串匹配KMP算法及求next值算法 2. 代码实现 ...

  9. 字符串匹配 KMP算法

    问题描述:字符串匹配即查找待匹配字符串(模式串)p在主串s中的位置.一般处理这种问题往往采用简单粗暴的方法--暴力匹配法.所谓暴力匹配法,就是对主串s的每一个字符与要匹配的字符串p的每个字符进行逐一匹 ...

最新文章

  1. 安装apr和tomcat-native优化tomcat
  2. [转载]日历设计之重复事件规则设计
  3. 十天学会ASP.net
  4. 实战SSM_O2O商铺_20【商铺编辑】View层开发
  5. MySQL利用存储过程清除所有表中的所有记录
  6. 含有运算符的STRING得到计算结果,类似JS的EVAL
  7. 计算机网络技术超文本,网络协议确定了计算机网络传递和管理信息的规范,其中HTTP属于()A、超文本传输协议B、传输控制协...
  8. java实体类属性非空判断工具类
  9. 一起学习C语言:函数(二)
  10. 计算机组成原理944考试,郑州大学2019年硕士研究生入学考试 《944计算机组成原理》考试大纲...
  11. 逻辑删除用户后 用户名重复怎么办?
  12. python数据分析学什么意思_什么是python数据分析
  13. c++实验总结_史上最全场景文字检测资源合集(70篇重要论文 + 15个开源代码 + 176个实验结果 + 1305个统计信息)...
  14. 快手分身多开黑屏脚本
  15. css3 的filer 功能
  16. FC4下安装plog快速指南(plog版本:1.01)
  17. python读取edi_对python .txt文件读取及数据处理方法总结
  18. python 今日头条 微头条_今日头条的入门,其实就是‘微头条’
  19. Tencent QQ皮肤图像文件格式Gft转png或bmp源代码
  20. 2018版 自考 计算机网络原理 04741 考试大纲

热门文章

  1. 使用OmniDB数据库管理工具,管理Oracle/MariaDB/PostgreSQL等关系型数据库
  2. 【sql那些事】时间处理的一揽子事
  3. 【Python爬虫】网络爬虫的“盗亦有道”
  4. linux安装包管理(未完待续)
  5. hexo博客添加暗色模式_我如何将暗模式添加到我的网站
  6. soa面向服务体系结构_服务和面向微服务的体系结构简介
  7. dagger2 注入_如何使用Dagger 2在您的应用程序中实现依赖注入
  8. mongodb python 大于_5、Python操作MongoDB
  9. mysql 本地登录失败 - 已授权
  10. 解决freeswitch ICE 获取RTP地址时间过长的问题