字符串匹配算法(一):BF(BruteForce)算法和RK(RabinKarp)算法
文章目录
- BF
- 思路
- 实现
- RK
- 思路
- 实现
字符串匹配是计算机科学中最古老、研究最广泛的问题之一。一个字符串是一个定义在有限字母表∑上的字符序列。例如,ATCTAGAGA是字母表∑ = {A,C,G,T}上的一个字符串。字符串匹配问题就是在一个大的字符串T中搜索某个字符串P的所有出现位置。
为了方便后面讲解,在这里先提出几个概念。
- 主串和模式串,主串即字符串的本体,模式串即用于比对的子串,即我们将要在主串中查找模式串。例如我们要在字符串A中查找字符串B,则A为主串,B为模式串。
- 单模式匹配算法:即在一个主串中匹配一个模式串,例如BF、RK、BM、KMP等算法就是单模式
- 多模式匹配算法:即在一个主串中匹配多个字符串,例如之前讲过的Trie树,以及之后会写的AC自动机。
BF
思路
BF算法是Brute Force的缩写,中文名叫做暴力匹配算法,故名思意,其算法简单粗暴,但对应的性能也不高,算法逻辑如下。
如上图,BF算法的思路就是从主串的每一个位置进行对比,如果当前位置模式串无法匹配,就移动到下一个位置,继续匹配
实现
该算法的实现十分简单,代码如下
#include<string>
#include<iostream>using namespace std;int bruteForce(const string& str, const string& pattern)
{//不满足条件则直接返回falseif(str.empty() || pattern.empty() || str.size() < pattern.size()){return -1;}int i = 0, j = 0;int len1 = str.size(), len2 = pattern.size();while(i < str.size()){while(j < pattern.size()){//如果当前不匹配if(str[i + j] != pattern[j]){i++; //主串移动到下一个位置j = 0; //模式串回到起始位置break;}j++; //如果模式串当前位置匹配,则继续匹配下一个位置}//如果模式串全部匹配,则返回匹配的位置if(j == pattern.size()){return i;}}return -1;
}
可以看出,这种算法存在着大量无意义的匹配,在最坏的情况下,我们需要依次匹配完主串中的所有位置,因此其时间复杂度高达O(N * M) N为主串长度,M为模式串长度。
RK
思路
那么有什么方法可以减少那些不必要的匹配,提高效率呢?这时候就可以巧妙地借助哈希算法来完成这个任务。
如果对于哈希不了解的可以查看我往期的博客
高级数据结构与算法 | 哈希 :哈希冲突、负载因子、哈希函数、哈希表、哈希桶
RK算法的全称是RabinKarp,是Rabin 和 Karp这两位科学家在BF算法的基础上,加上了哈希的思想后是实现的。
BF算法的主要缺陷在于模式串会去尝试匹配主串的任何一个位置,并且其中每次的匹配都会去一个一个字符进行对比,导致效率的下降。
而RK算法则想到了一个好方法,可以先进行一个预匹配,即哈希值的匹配,如果哈希值不同则说明字符串不同,没有比较的意义,而当哈希值相同时,为了避免哈希冲突的情况,再进行字符串的匹配即可。
由于哈希值只是一个整型,其比较起来的代价比起字符串大大的降低了
不过,哈希值的计算也需要通过遍历字符串来获得,那么效率不是又变低了吗?这时,就可以通过合理的设计哈希函数,来解决这个问题。
在这里,我选择使用最简单的按位相加来进行举例
首先,我们将模式串以及主串中第一个用来匹配的子串的哈希值计算出来,进行比较。
int hashFunc(const string& str)
{int hashCode = 0;for(int i = 0; i < str.size(); i++){hashCode += (str[i] - 'a');}return hashCode;
}
当其不匹配时,我们需要匹配主串的下一个位置,由于字符串发生变化,哈希值也应该发生变化。
考虑到匹配子串的变化起始就是从主串中向后移动一位,也就是删除了最高位,增加了最低位,所以我们可以借助这个性质,进行增量计算,即减去首位的哈希值,加上末尾的哈希值,就可以避免每次都要重复计算哈希值的问题。
//获取移动到下一个位置后的字符串哈希值,即减去开头的哈希值,加上末尾的哈希值
int nextHash(const string& str, int hash, int begin, int end)
{hash -= (str[begin] - 'a');hash += (str[end] - 'a');return hash;
}
上面这种哈希算法的计算十分简单,但是也存在着大量的哈希冲突,所以选择一个合理的哈希算法,对效率的提升有很大的帮助,但是每种哈希算法也存在着一定的缺陷,例如26进制,虽然其几乎避免了哈希冲突,但是在字符串过长时会导致哈希值的溢出,所以要结合使用场景对哈希函数进行选择。
由于这里只是算法的一个介绍,就直接使用这种最简单的哈希函数。
此时,整个RK算法就包含两个部分,第一部分是进行哈希值的计算,时间复杂度为O(N),第二部分就是进行字符串的匹配,因为哈希值的比较的时间复杂度为O(1),而哈希值的更新也只是简单的增量计算,也是O(1),所以整个算法的时间复杂度为O(N),但是在最坏情况下,如存在大量的哈希冲突时,每次都需要将进行字符串的对比,这时的时间复杂度就会退化回O(N * M)
实现
算法实现如下
#include<string>
#include<iostream>using namespace std;//字符串哈希函数,这里使用的是最简单的按位相加
int hashFunc(const string& str)
{int hashCode = 0;for(int i = 0; i < str.size(); i++){hashCode += (str[i] - 'a');}return hashCode;
}//获取移动到下一个位置后的字符串哈希值,即减去开头的哈希值,加上末尾的哈希值
int nextHash(const string& str, int hash, int begin, int end)
{hash -= (str[begin] - 'a');hash += (str[end] - 'a');return hash;
}int rabinKarp(const string& str, const string& pattern)
{//不满足条件则直接返回falseif(str.empty() || pattern.empty() || str.size() < pattern.size()){return -1;}int len1 = str.size(), len2 = pattern.size();int patternHash = hashFunc(pattern);int subHash = hashFunc(str.substr(0, len2));for(int i = 0; i < (len1 - len2 + 1); i++){//如果当前的哈希值相同,则遍历比较字符串是否也相同,如果不同则没有必要进行比较if(patternHash == subHash && pattern == str.substr(i, len2)){return i; //如果当前匹配,则返回匹配主串的起始位置}//如果主串中剩余字符还能与模式串进行对比,则更新哈希值if(len1 - i > len2){subHash = nextHash(str, subHash, i, i + len2);}}return -1;
}
字符串匹配算法(一):BF(BruteForce)算法和RK(RabinKarp)算法相关推荐
- 数据结构与算法之美笔记——基础篇(下):图、字符串匹配算法(BF 算法和 RK 算法、BM 算法和 KMP 算法 、Trie 树和 AC 自动机)
图 如何存储微博.微信等社交网络中的好友关系?图.实际上,涉及图的算法有很多,也非常复杂,比如图的搜索.最短路径.最小生成树.二分图等等.我们今天聚焦在图存储这一方面,后面会分好几节来依次讲解图相关的 ...
- 最长不下降子序列的O(n^2)算法和O(nlogn)算法
转帖 最长不下降子序列的O(n^2)算法和O(nlogn)算法 最长不下降子序列(LIS:Longest Increasing Subsequence) //用句通俗的话说,我讲的很通俗易懂~~ 问题 ...
- WordCount作业提交到FileInputFormat类中split切分算法和host选择算法过程源码分析
参考 FileInputFormat类中split切分算法和host选择算法介绍 以及 Hadoop2.6.0的FileInputFormat的任务切分原理分析(即如何控制FileInputForm ...
- KNN算法和Kernel KNN算法的区别
KNN算法和Kernel KNN算法的区别 KNN算法 KNN(K-Nearest Neighbor,简称KNN)算法,是一种常用的监督学习方法,其工作机制为:给定测试样本,基于某种距离度量找出训练集 ...
- 贪心算法和01背包算法
贪心算法和01背包算法 实验报告 1.问题 2.解析 3.设计 4.分析 5.源码 实验报告 课程名称 <算法分析与设计> 实验名称 贪心算法和01背包算法 1.问题 [描述算法问题,首选 ...
- 并行sgd算法和min-batch gd算法
sgd算法全称随机梯度下降法,有着比批梯度下降法更快收敛的优势,该算法名称中的"随机"二字是改算法的中心精神所在. sgd算法是一种天生的串行的算法,当数据量大的时候们希望通过使用 ...
- 【算法篇-字符串匹配算法】BF算法和KMP算法
目录 前言 1. BF算法 1.1 画图分析 1.3 BF 算法的时间复杂度 2. KMP 算法 2.1 KMP 算法和 BF 算法 的区别 2.1.1 为什么主串不回退? 2. 2 next 数组 ...
- 数据结构之字符串匹配算法(BF算法和KMP算法)
字符串匹配算法: 就是给定两个串,主串(s)和子串(sub), 查找子串是否在主串里面,如果找到便返回子串在主串中第一个元素的位置下标,否贼返回-1,. 在这里我 们讨论的时候主要用字符串来举例实现. ...
- 数据结构与算法分析(十六)--- 如何设计更高效的字符串匹配算法?(BF + RK + KMP + BMH)
文章目录 一.Brute Force 匹配算法 二.Rabin–Karp 匹配算法 三.Knuth–Morris–Pratt 匹配算法 四.Boyer-Moore-Horspool 匹配算法 五.字符 ...
最新文章
- C# TTS-文本转语音
- java条码大小_java – 自定义条形码输入中缺少条形码高度
- ECharts - 地图
- 如果王思聪是产品经理
- 用命令导入导出MS SQL数据
- java中计算明年今天的日期_计算今天之后的下一个周年日
- 命名实体识别_命名实体识别的几种标注形式
- 鸿蒙车载智慧屏评测,华为智慧屏S Pro体验:告诉你鸿蒙OS有多优秀?
- 大数据分析,利用向外扩展技术深入挖掘商业价值
- 关系型数据库一致性的理解
- Linux meset
- 基于html的美食网站——速鲜站餐饮食品(HTML+CSS+JavaScript)大学生网页制作教程 表格布局网页模板 学生HTML静态美食网页设计作业成品 简单网页制作代码 学生美食网页作品
- matlab kdj,kdj指标详什么时候买入,kdj指标详解四大绝技
- Java8 JDK8
- python 豆瓣电影top250_python 爬豆瓣电影top250
- [Linux]进程概念以及进程状态
- python取元素_python 如何提取对象内的元素
- SSL集训 某OJ2021.08.14 提高B组 Luogu P7527 [USACO21OPEN] United Cows of Farmer John G【树状数组】
- 使用opencv+python识别七段数码显示器的数字识别
- 还想抄底?小心爆炸!