C++ : KMP 字符串匹配算法
在传统字符串匹配中我们求得字符串p出现在字符串s中的位置。我们把字符串s称为主串,字符串p称为模式串。
KMP算法的原理简单来说就是匹配的时候不回溯主串的指针i,而只回溯模式串指针j ,即匹配过程中,不移动主串,指移动模式串来达到"尽可能向右滑动最大的距离"。
s[num] | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
主串s | a | c | a | b | a | a | b | a | a | b | c | a | c | a | a | b | c | ||||||
模式串p | a | b | a | a | b | c |
图1.
假如模式串p中的c与s[11] 失配了,那么再传统匹配算法中,是拿s[7]和p[0] (a)去匹配,而在KMP匹配中是那s[11]和p[2] (a)中去匹配。因为在这次失配中可知,主串s中6~10的值就是abaab,再拿p[0]和s[7] 匹配必定失配,这次比较就显得多余。那么我们怎么知道失配的时候,模式串要向右移多少个单位呢?
假设图1此时主串要正在匹配的位置是 i =11 ,而 与 p[j]=='c',即j==5产生了失配,那么j指针要回溯到 某个k,假如=2值时要进行下一步匹配 ,那么k值必须满足两个关系式
由图一可知 p[3]p[4]=s[9]s[10],
由图二可知 p[0]p[1]=s[9]s[10]
s[num] | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
主串s | a | c | a | b | a | a | b | a | a | b | c | a | c | a | a | b | c | ||||||
模式串p | a | b | a |
图2.
换成通用表达式就是
即
用逻辑意义上来解释公式3:就是当在模式串匹配第j个要回溯到k时此时j和k满足 前缀k个值和后缀k个值要相同。满足这条件的最大k值即为将要回溯的位置,假如用next数组表示模式串要回溯的位置其中 next[j]=k。
所以实际上,模式串中的next[j]的值与主串s是无关的。
假设next[j]=k即求next[j+1]
如果此时P[j] = P[k] 那么next[j+1]=k+1 即 next[j+1]= next[j] +1
如果此时 P[j] != P[k] ,那么就要移动next[k]个字符进行比较,达到s[i] = p[k],前缀与后缀对齐的目的。相等则next[j+1]=next[k]+1,不相等则迭代 k=next[k]重新移动比较,例如结合图3和图4,next[5]=2,求next[6]?
因为next[5]=2,所以比较出P[5] !=P[2],前缀无法对齐后缀就继续向右移(回溯),
因为next[2]=0,所以比较出P[5] !=p[0],
因为next[0]=-1接下来比对P[5]和P[-1] 因为next[0] = -1;
当不存在可回溯的k时next[j+1] = -1 ;
在存在一个可回溯的位置0<k'<k<j 那么
while(k'>0 && k'< k ){if(k' = -1){next[j+1] = 0;
}if(p[k'] != p[k]){k= next[k]}else{next[j+1] = next[k] +1
}
}
s[num] | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
主串s | a | c | a | b | a | a | b | a | a | b | c | a | c | a | a | b | c | ||||||
模式串p | a | b | a | a | b | c | a |
图3
s[num] | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
主串s | a | c | a | b | a | a | b | a | a | b | c | a | c | a | a | b | c | ||||||
模式串p | a | b | a | a | b | c | a | c |
图4.
.
s[num] | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
主串s | a | c | a | b | a | a | b | a | a | b | c | a | c | a | a | b | c | ||||||
模式串p | a | b | a | a | b | c | a | c |
图5.
.所以字符串abaabcac的next值为 next [] = {-1,0,0,1,1,2,0,1};
改进的next ,
例如字符串 aaaab的next []={-1,0,1,2,3};
s[num] | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
主串s | |||||||||||||||||||||||
模式串p | a | a | a | a | b |
KMP算法中当s[9] 和p[3] (b)比较失配发生回溯时,接下来s[9] 会和p[2]比较,此时这个比较是多余的必定会再次失配 。把多余的比较剔除掉就是改进的next。
如果回溯的值与原值相等即, P[j] = P[k] 时直接 另 P[j] = P[next[k]];
hpp:
//
// Created by hu on 2020/8/5.
//#ifndef SMARTDONGLIB_SSTRING_H
#define SMARTDONGLIB_SSTRING_H
#include <string>
#include <vector>namespace SmartDongLib {class SString {public:SString(){ str_="";}explicit SString(std::string str): str_(std::move(str)){}SString(const SString& str){ str_=str.str_;}int length(){return str_.length();}SString copy(){return *this;};bool isEmpty(){return str_.empty();}void clear(){str_=""; }SString concat(const SString& str2){ SString ret(str_ + str2.str_); return ret;}SString subString(int pos, int len){SString ret (str_.substr(pos, len)); return ret;}SString subString(int pos=0){SString ret (str_.substr(pos)); return ret;}int index( const SString& , int pos =0);int index_KMP(SString& str2, int pos=0);SString replace(SString src, const SString& target);void strinsert(int pos,const SString& T){str_.insert(pos, T.str_);}void strdelete(int pos,int len){str_.erase(pos,len);}SString operator+(std::string str1){ return SString(str_ + std::move(str1));}SString operator+=(std::string str1){ return SString(str_ + std::move(str1));}bool operator==(const std::string& str1){return str_==str1;}std::string get(){return str_;}void getnext(int next[]);private:std::string str_;};
}#endif //SMARTDONGLIB_SSTRING_H
cpp :
//
// Created by hu on 2020/8/5.
//#include "sdstructure/linearlist/SString.h"
namespace SmartDongLib {inline int SString::index(const SString& str2, int pos ) {int iptr = pos , jptr = 0;while (iptr < str_.length() && jptr<str2.str_.length()){if (str_[iptr] == str2.str_[jptr]){//相等则比较下一位iptr++ ; jptr++;} else{//不相等则回溯,模式串指针从0 开始 i 回溯到原先的起始值+1 , 现值i'与原先的起始值i 满足 i'-i=j'-j其中j=0iptr = iptr - jptr+1;jptr = 0;}}if (jptr >=str2.str_.length()){return iptr - jptr;}return -1;}/*** <p> 1 . 求next数组,有了next数组后一个一个匹配,如果失配让 j = next[j];* @param substr* @param pos* @return*/inline int SString::index_KMP(SString& substr, int pos) {int i=pos, j=0;int next[substr.str_.length()];substr.getnext(next);int thisLen=length(),sublen=substr.length();while ( i < thisLen && j < sublen){if (j==-1 || str_[i] == substr.str_[j]){i++;j++;} else{j=next[j];}}if (j >= sublen){int ret =i-sublen;return ret;}return -1;}inline SString SString::replace(SString src, const SString& target) {if (src.str_.empty()){return *this;}int index=0;while ( index != -1) {index = index_KMP(src);if(index != -1) {str_.erase(index, src.str_.length());str_.insert(index, target.str_);}}return *this;}/*** <p>原理: 当求next的第j个元素时,看 j-1 个元素开始和第0个元素比对,k不断增加取最大值满足 0<k<j* 从后往前数k个即第 j-k+1...j-1元素与 0...k-1* 如 abaabcac 当(a)j=0 next[0]=0 ; (b)j =1 ,next[1]=1,;(a) j=2时,k=1,第1个元素和第0个元素比对即a和b比不对就是1* 当(a)j=3,k=1,第2个元素和第0个元素 比a和a匹配上了 那就是next[3]=2;* @param substr* @return*/inline void SString::getnext(int next[]) {const int len =str_.length();
// next.resize(len,-1);int i = 0,j=-1;next[0]=-1;while (i<len){if (j==-1 ||str_[i] == str_[j]){++i;++j;next[i]=j;} else{j=next[j];}}//return next;}
}
example:
//
// Created by hu on 2020/8/6.
//#include <iostream>
#include "sdstructure/linearlist/SString.cpp"
using namespace std;
using namespace SmartDongLib;
int main(){SString mainstr("acabaabaabcacaabc");SString substr("abaabcac");//-1 0 -1 1 0 2 -1 1SString target("ijn");int next[substr.length()];substr.getnext(next);for (int i :next){cout<<i<<" ";}int index=mainstr.index_KMP(substr);int index2=mainstr.index(substr);cout<<endl<<index;cout<<endl<<index2;mainstr.replace(substr,target);cout<<endl<<mainstr.get();
}
C++ : KMP 字符串匹配算法相关推荐
- KMP字符串匹配算法理解(转)
一.引言 主串(被扫描的串):S='s0s1...sn-1',i 为主串下标指针,指示每回合匹配过程中主串的当前被比较字符: 模式串(需要在主串中寻找的串):P='p0p1...pm-1',j 为模式 ...
- 字符串匹配KMP算法设计C语言,KMP字符串匹配算法笔记
网上有很多解释KMP算法的文章,A_B_C_ABC的这篇很详细,反复看了好几遍,总算理解了个大概,但是总觉得没那么爽快.其实,一种算法各人有各人的理解方法,找到适合自己理解的才容易记住.下面是我对这个 ...
- Java实现算法导论中KMP字符串匹配算法
"前缀"和"后缀". "前缀"指除了最后一个字符以外,一个字符串的全部头部组合:"后缀"指除了第一个字符以外,一个字符串 ...
- 每周一算法之六——KMP字符串匹配算法
KMP是一种著名的字符串模式匹配算法,它的名称来自三个发明人的名字.这个算法的一个特点就是,在匹配时,主串的指针不用回溯,整个匹配过程中,只需要对主串扫描一遍就可以了.因此适合对大字符串进行匹配. 搜 ...
- 【KMP】KMP 字符串匹配算法
文章目录 1.概述 2.暴力匹配算法 3.KMP算法 3.1 案例 3.2 部分匹配表 3.3 匹配 M.参考 本文为博主九师兄(QQ:541711153 欢迎来探讨技术)原创文章,未经允许博主不允许 ...
- 字符串匹配算法(KMP)
文章目录 1. KMP由来 2. KMP算法基本原理 3. 代码 4. Leetcode 28. 实现 strStr() 1. KMP由来 上一节说的BM算法是最高效.最常用的字符串匹配算法. 最知名 ...
- 字符串匹配算法之KMP
目录 需求 基础知识 逻辑解析 源码实现 需求 先简单描述溪源曾经遇到的需求: 需求一:项目结果文件中实验结论可能会存在未知类型.转换错误.空指针.超过索引长度等等.这里是类比需求,用日常开发中常出现 ...
- 数据结构与算法分析(十六)--- 如何设计更高效的字符串匹配算法?(BF + RK + KMP + BMH)
文章目录 一.Brute Force 匹配算法 二.Rabin–Karp 匹配算法 三.Knuth–Morris–Pratt 匹配算法 四.Boyer-Moore-Horspool 匹配算法 五.字符 ...
- C++实现KMP(Knuth-Morris-Pratt)字符串匹配算法
#include"iostream"using namespace std;// 暴力匹配算法 bool string_match(char *mains,unsigned int ...
最新文章
- Android之子菜单的创建
- python属性和方法的区别_Python中几种属性访问的区别
- LeetCode 1061. 按字典序排列最小的等效字符串(并查集)
- ubuntu14.04下 安装matlabR2015b遇到的一些问题及其解决方法
- JS 实现 Tab标签切换功能
- 三维点云学习(3)6- 实现K-Means
- XP系统优化超简单实用版
- 【python】短信验证之腾讯云短信验证详细步骤
- 《SEM长尾搜索营销策略解密》一一2.4 长尾的主动与被动
- 转录组测序day 1 基础知识
- iOS 判断机型是否为iPhone Xs
- 网站服务器登录很慢,网站打开速度慢如何解决?有何技巧?
- Apriori关联分析算法 -尿布与啤酒的故事
- 算法.动态规划 导航/数塔取数字问题
- 内网渗透(五十)之域控安全和跨域攻击-使用其他工具导出域账号和散列值
- Word学习笔记分享
- 14届蓝桥杯Python总结
- 迅雷播放插件下载的在线字幕位置
- 图像超分算法SRLUT: Practical Single-Image Super-Resolution Using Look-Up Table图像超分辨率重建
- 手机相机里面的m_让手机具备M档 WP专业拍照软件ProShot