字符串类——KMP子串查找算法
1, 如何在目标字符串 s 中,查找是否存在子串 p(本文代码已集成到字符串类——字符串类的创建(上)中,这里讲述KMP实现原理) ?
1,朴素算法:

2,朴素解法的问题:


1,问题:有时候右移一位是没有意义的;
2,KMP 算法可以右移一定的位数,提高效率;
3,朴素算法和 KMP 算法对比示例图:

2,伟大的发现(KMP):
1,匹配失败时的右移位数与子串本身相关,与目标无关;
2,移动位数 = 已匹配的字符数 - 对应的部分匹配值;
1,“已匹配的字符数”已知,“对应的部分匹配值”未知;
(2),部分匹配值就是对应元素和从开始元素开始连续相同的个数;
3,任意子串都存在一个唯一的部分匹配值;
3,部分匹配表示例:

4,部分匹配表如何获得 ?
1,前缀集:
1,除了最后一个字符外,一个字符串的全部头部组合;
2,后缀集:
1,除了第一个字符以外,一个字符串的全部尾部组合;
3,部分匹配值:
1,前缀集和后缀集最长共有元素的长度;
(2),得到共有长度是为了得到对应各个位置前面不相同的元素个数,这样如果前面不同元素匹配了,那么就可以直接移动的了;
4,ABCDABD 部分匹配表示例:

5,怎么编程产生部分匹配表(Partial Matched Table)(递推完成)?
1,实现关键:
1,PMT[1] = 0(下标为 0 的元素匹配值为 0);
2,从 2 个字符开始递推(从下标为 1 的字符开始递推);
3,假设 PMT[n] = PMT[n-1] + 1(最长共有元素的长度)(这是一个贪心假设);
4,当假设不成立,PMT[n] 在 PMT[n-1](这里是指的第 PMT[n-1] 个元素)个元素的 ll 值上用种子作为扩展的基础上继续比对;
1,当当前前子集“前后集最长共有元素数”为 0 时,说明其元素都不相等,可以直接比较当前子集延长一字母序列的首尾字母,且此子集“前后集最长共有元素数”最大为 1;
2,将 “前后集最长共有元素数”对应的前后缀最为种子扩展;
3,假设不成立时,把已经匹配的前 PMT[n-1] 个元素的“前后集最长共有元素数”(因为必须在相同的位置上扩展才有意义)作为种子来扩展;
6,部分匹配表的递推与实现:
1,部分匹配表的递推:

2,在 String 中实现部分匹配表:
/* 建立指定字符串的 pmt(部分匹配表)表 */
int* String::make_pmt(const char* p) // O(m),只有一个 for 循环
{
int len = strlen(p);
int* ret = static_cast<int*>(malloc(sizeof(int) * len)); if ( ret != NULL )
{
int ll = ; //定义 ll,前缀和后缀交集的最大长度数,largest length;第一步
ret[] = ; // 长度为 1 的字符串前后集都为空,对应 ll 为 0; for(int i=; i<len; i++) // 从第一个下标,也就是第二个字符开始计算,因为第 0 个字符前面已经计算过了; 第二步
{
/* 算法第四步 */
while( (ll > ) && (p[ll] != p[i]) ) // 当 ll 值为零时,转到下面 if() 函数继续判断,最后赋值与匹配表,所以顺序不要错;
{
ll = ret[ll - ]; // 从之前匹配的部分匹配值表中,继续和最后扩展的那个字符匹配
} /* 算法的第三步,这是成功的情况 */
if( p[ll] == p[i] ) // 根据 ll 来确定扩展的种子个数为 ll,而数组 ll 处就处对应的扩展元素,然后和最新扩展的元素比较;
{
ll++; // 若相同(与假设符合)则加一
} ret[i] = ll; // 部分匹配表里存储部分匹配值 ll
}
} return ret;
}
7,部分匹配表的使用(KMP 算法):

1,不匹配时,移动位置,之后直接从/字符串的/不匹配前字符/的部分匹配值下标处/开始匹配;
8,KMP 子串查找算法在 String 中的实现 :
/* 在字符串 s 中查找子串 p */
int String::kmp(const char* s, const char* p) // O(m) + O(n) ==> O(m+n), 只有一个 for 循环
{
int ret = -;
int sl = strlen(s);
int pl = strlen(p);
int* pmt = make_pmt(p); if( (pmt != NULL) && ( < pl) && (pl <= sl) ) // 判断查找条件
{
for(int i=, j=; i<sl; i++) // i 的值要小于目标窜长度才可以查找
{
while( (j > ) && (s[i] != p[j]) ) // 比对不上的时候,持续比对,
{
j = pmt[j-];//移动后应该继续匹配的位置,j =j-(j -LL)=LL = PMT[j-1]
} if( s[i] == p[j] ) // 比对字符成功
{
j++; // 加然后比对下一个字符
} if( j == pl ) // 这个时候是查找到了,因为 j 增加到了 pl 的长度;
{
ret = i + - pl; // 匹配成功后,i 的值停在最后一个匹配成功的字符上,这样就返回匹配成功的位置 break;
}
}
} free(pmt); return ret;
}
9,小结:
1,部分匹配表是提高子串查找效率的关键;
2,部分匹配值定义为前缀和后缀最长共有元素的长度;
3,可以用递推的方法产生部分匹配表;
4,KMP 利用部分匹配值与子串移动位数的关系提高查找效率;
1,每次匹配失败的时候,子串不会简单的右移一位,而是查询部分匹配表中的值,查到后则右移一定位数,使算法效率由平方变成线性时间;
字符串类——KMP子串查找算法的更多相关文章
- 数据结构开发(14):KMP 子串查找算法
0.目录 1.KMP 子串查找算法 2.KMP 算法的应用 3.小结 1.KMP 子串查找算法 问题: 如何在目标字符串S中,查找是否存在子串P? 朴素解法: 朴素解法的一个优化线索: 示例: 伟大的 ...
- 第四十一课 KMP子串查找算法
问题: 右移的位数和目标串没有多大的关系,和子串有关系. 已匹配的字符数现在已经有了,部分匹配值还没有. 前六位匹配成功就去查找PMT中的第六位. 现在的任务就是求得部分匹配表. 问题:怎么得到部分匹 ...
- 第41课 kmp子串查找算法
1. 朴素算法的改进 (1)朴素算法的优化线索 ①因为 Pa != Pb 且Pb==Sb:所以Pa != Sb:因此在Sd处失配时,子串P右移1位比较没有意义,因为前面的比较己经知道了Pa != Sb ...
- 字符串类——KMP算法的应用
1,字符串类中的新功能(本文代码已集成到字符串类——字符串类的创建(上)中,这里讲述函数实现原理): 2,子串查找(KMP 算法直接运用): 1,int indexOf(const char* s) ...
- C++自定义String字符串类,支持子串搜索
C++自定义String字符串类 实现了各种基本操作,包括重载+号实现String的拼接 findSubStr函数,也就是寻找目标串在String中的位置,用到了KMP字符串搜索算法. #includ ...
- 常见查找算法之php, js,python版
常用算法 >>>1. 顺序查找, 也叫线性查找, 它从第一个记录开始, 挨个进行对比, 是最基本的查找技术 javaScript 版顺序查找算法: // 顺序查找(线性查找) 只做找 ...
- 串、串的模式匹配算法(子串查找)BF算法、KMP算法
串的定长顺序存储#define MAXSTRLEN 255,//超出这个长度则超出部分被舍去,称为截断 串的模式匹配: 串的定义:0个或多个字符组成的有限序列S = 'a1a2a3…….an ' n ...
- KMP 算法 & 字符串查找算法
KMP算法 Knuth–Morris–Pratt algorithm 克努斯-莫里斯-普拉特 算法 algorithm kmp_search: input: an array of character ...
- 【数据结构】 字符串&KMP子串匹配算法
字符串 作为人机交互的途径,程序或多或少地肯定要需要处理文字信息.如何在计算机中抽象人类语言的信息就成为一个问题.字符串便是这个问题的答案.虽然从形式上来说,字符串可以算是线性表的一种,其数据储存区存 ...
随机推荐
- [2019杭电多校第七场][hdu6651]Final Exam
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6651 题意:n个科目,总共m分,通过一科需要复习花费科目分数+1分钟,在不知道科目分数的情况下,问最少 ...
- BCR-ABL融合基因及检测
费城染色体 费城染色体(Philadelphia chromosome, Ph (or Ph') chromosome),或称费城染色体易位(Philadelphia translocation),是 ...
- 10-js对象、数组
# js对象 ```1.使用原始的方式创建内置对象 var myObject = new Object(); myObject.name = “lijie”; myObject.age = 20; m ...
- Java中的集合详解及代码测试
1:对象数组 (1)数组既可以存储基本数据类型,也可以存储引用类型.它存储引用类型的时候的数组就叫对象数组. 2:集合(Collection) (1)集合的由来 我们学习的是Java -- 面向对象 ...
- MemCache--01 解决session
目录 1. MemCache介绍 2.Session与Cookie介绍 3. 安装部署Nginx 4. 安装部署PHP 5. 安装Mariadb数据库 6. 部署网站(phpMyAdmin) 7. 部 ...
- python实现通过企业微信发送消息
实现了通过企业微信发送消息,平时用于运维的告警还是不错的,相对于邮件来说,实时性更高,不过就是企业微信比较麻烦,此处不做过多解释. 企业微信api的详细请看:http://work.weixin.qq ...
- 02机器学习实战之K近邻算法
第2章 k-近邻算法 KNN 概述 k-近邻(kNN, k-NearestNeighbor)算法是一种基本分类与回归方法,我们这里只讨论分类问题中的 k-近邻算法. 一句话总结:近朱者赤近墨者黑! k ...
- 使用GDB调试时attach ID不被允许
在进入gdb后,直接使用attach ID,出现下面的情况: Could not attach to process. If your uid matches the uid of the targ ...
- ps:矢量格式图像
假设我们写了一首新的乐曲,要把它交给唱片公司,可以通过两种方式: 把这首乐曲弹奏出来并录制在磁带上. 把这首乐曲的乐谱写下来. 这两种方式的最大区别在于记录的形式. 前者是记述性的.包含乐曲的音频信息 ...
- 前端每日实战:30# 视频演示如何用纯 CSS 创作一个晃动的公告板
效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/wjZoGV 可交互视频教程 此视频 ...