通过上一节的介绍,学习了普通模式匹配算法,大体思路是:模式串从主串的第一个字符开始匹配,每匹配失败,主串中记录匹配进度的指针 i 都要进行 i-j+1 的回退操作(这个过程称为“指针回溯”),同时模式串向后移动一个字符的位置。一次次的循环,直到匹配成功或者程序结束。

"KMP"算法相比于"BF"算法,优势在于:

在保证指针 i 不回溯的前提下,当匹配失败时,让模式串向右移动最大的距离;

并且可以在O(n+m)的时间数量级上完成对串的模式匹配操作;

故,"KMP"算法称为“快速模式匹配算法”。

模式串向右移动距离的计算

在模式串和主串匹配时,各有一个指针指向当前进行匹配的字符(主串中是指针 i ,模式串中是指针 j ),在保证 i 指针不回溯的前提下,如果想实现功能,就只能让 j 指针回溯。

j 指针回溯的距离,就相当于模式串向右移动的距离。 j 指针回溯的越多,说明模式串向右移动的距离越长。

计算模式串向右移动的距离,就可以转化成:当某字符匹配失败后, j 指针回溯的位置。

对于一个给定的模式串,其中每个字符都有可能会遇到匹配失败,这时对应的 j 指针都需要回溯,具体回溯的位置其实还是由模式串本身来决定的,和主串没有关系。

模式串中的每个字符所对应 j 指针回溯的位置,可以通过算法得出,得到的结果相应地存储在一个

计算方法是:对于模式串中的某一字符来说,提取它前面的字符串,分别从字符串的两端查看连续相同的字符串的个数,在其基础上 +1 ,结果就是该字符对应的值。

每个模式串的第一个字符对应的值为 0 ,第二个字符对应的值为 1 。

例如:求模式串 “abcabac” 的 next 。前两个字符对应的 0 和 1 是固定的。

对于字符 ‘c’ 来说,提取字符串 “ab” ,‘a’ 和 ‘b’ 不相等,相同的字符串的个数为 0 ,0 + 1 = 1 ,所以 ‘c’ 对应的 next 值为 1 ;

第四个字符 ‘a’ ,提取 “abc” ,从首先 ‘a’ 和 ‘c’ 就不相等,相同的个数为 0 ,0 + 1 = 1 ,所以,‘a’ 对应的 next 值为 1 ;

第五个字符 ‘b’ ,提取 “abca” ,第一个 ‘a’ 和最后一个 ‘a’ 相同,相同个数为 1 ,1 + 1 = 2 ,所以,‘b’ 对应的 next 值为 2 ;

第六个字符 ‘a’ ,提取 “abcab” ,前两个字符 “ab” 和最后两个 “ab” 相同,相同个数为 2 ,2 + 1 = 3 ,所以,‘a’ 对应的 next 值为 3 ;

最后一个字符 ‘c’ ,提取 “abcaba” ,第一个字符 ‘a’ 和最后一个 ‘a’ 相同,相同个数为 1 ,1 + 1 = 2 ,所以 ‘c’ 对应的 next 值为 2 ;

所以,字符串 “abcabac” 对应的 next 数组中的值为(0,1,1,1,2,3,2)。

上边求值过程中,每次都需要判断字符串头部和尾部相同字符的个数,而在编写算法实现时,对于某个字符来说,可以借用前一个字符的判断结果,计算当前字符对应的 next 值。

具体的算法如下:

模式串T为(下标从1开始):“abcabac”

next数组(下标从1开始):    01

第三个字符 ‘c’ :由于前一个字符 ‘b’ 的 next 值为 1 ,取 T[1] = ‘a’ 和 ‘b’ 相比较,不相等,继续;由于 next[1] = 0,结束。 ‘c’ 对应的 next 值为1;(只要循环到 next[1] = 0 ,该字符的 next 值都为 1 )

模式串T为:                  “abcabac”

next数组(下标从1开始):011

第四个字符 ’a‘ :由于前一个字符 ‘c’ 的 next 值为 1 ,取 T[1] = ‘a’ 和 ‘c’ 相比较,不相等,继续;由于 next[1] = 0 ,结束。‘a’ 对应的 next 值为 1 ;

模式串T为:                  “abcabac”

next数组(下标从1开始):0111

第五个字符 ’b’ :由于前一个字符 ‘a’ 的 next 值为 1 ,取 T[1] = ‘a’ 和 ‘a’ 相比较,相等,结束。 ‘b’ 对应的 next 值为:1(前一个字符 ‘a’ 的 next 值) + 1 = 2 ;

模式串T为:                  “abcabac”

next数组(下标从1开始):01112

第六个字符 ‘a’ :由于前一个字符 ‘b’ 的 next 值为 2,取 T[2] = ‘b’ 和 ‘b’ 相比较,相等,所以结束。‘a’ 对应的 next 值为:2 (前一个字符 ‘b’ 的 next 值) + 1 = 3 ;

模式串T为:                  “abcabac”

next数组(下标从1开始):011123

第七个字符 ‘c’ :由于前一个字符 ‘a’ 的 next 值为 3 ,取 T[3] = ‘c’ 和 ‘a’ 相比较,不相等,继续;由于 next[3] = 1 ,所以取 T[1] = ‘a’ 和 ‘a’ 比较,相等,结束。‘a’ 对应的 next 值为:1 ( next[3] 的值) + 1 = 2 ;

模式串T为:                  “abcabac”

next数组(下标从1开始):0111232

算法实现:

#include

#include

void Next(char*T,int *next){

int i=1;

next[1]=0;

int j=0;

while (i

if (j==0||T[i-1]==T[j-1]) {

i++;

j++;

next[i]=j;

}else{

j=next[j];

}

}

}

注意:在此程序中,next 数组使用的下标初始值为 1 ,next[0] 没有用到(也可以存放 next 数组的长度)。而串的存储是从数组的下标 0 开始的,所以程序中为 T[i-1] 和 T[j-1]。

基于next的KMP算法的实现

先看一下 KMP 算法运行流程(假设主串:ababcabcacbab,模式串:abcac)。

第一次匹配:

匹配失败,i 指针不动,j = 1(字符‘c’的next值);

第二次匹配:

相等,继续,直到:

匹配失败,i 不动,j = 2 ( j 指向的字符 ‘c’ 的 next 值);

第三次匹配:

相等,i 和 j 后移,最终匹配成功。

使用普通算法,需要匹配 6 次;而使用 KMP 算法,则只匹配 3 次。

实现代码:

int KMP(char * S,char * T){

int next[10];

Next(T,next);//根据模式串T,初始化next数组

int i=1;

int j=1;

while (i<=strlen(S)&&j<=strlen(T)) {

//j==0:代表模式串的第一个字符就和指针i指向的字符不相等;S[i-1]==T[j-1],如果对应位置字符相等,两种情况下,指向当前测试的两个指针下标i和j都向后移

if (j==0 || S[i-1]==T[j-1]) {

i++;

j++;

}

else{

j=next[j];//如果测试的两个字符不相等,i不动,j变为当前测试字符串的next值

}

}

if (j>strlen(T)) {//如果条件为真,说明匹配成功

return i-(int)strlen(T);

}

return -1;

}

KMP算法完整代码

#include

#include

void Next(char*T,int *next){

int i=1;

next[1]=0;

int j=0;

while (i

if (j==0||T[i-1]==T[j-1]) {

i++;

j++;

next[i]=j;

}else{

j=next[j];

}

}

}

int KMP(char * S,char * T){

int next[10];

Next(T,next);//根据模式串T,初始化next数组

int i=1;

int j=1;

while (i<=strlen(S)&&j<=strlen(T)) {

//j==0:代表模式串的第一个字符就和当前测试的字符不相等;S[i-1]==T[j-1],如果对应位置字符相等,两种情况下,指向当前测试的两个指针下标i和j都向后移

if (j==0 || S[i-1]==T[j-1]) {

i++;

j++;

}

else{

j=next[j];//如果测试的两个字符不相等,i不动,j变为当前测试字符串的next值

}

}

if (j>strlen(T)) {//如果条件为真,说明匹配成功

return i-(int)strlen(T);

}

return -1;

}

int main() {

int i=KMP("ababcabcacbab","abcac");

printf("%d",i);

return 0;

}

运行结果:

6

升级版的next

注意:KMP 算法的关键在于 next 数组的确定,其实对于上边的KMP算法中的next数组,不是最精简的,还可以简化。

例如:

模式串T:a b c a c

next  :0 1 1 1 2

在模式串“abcac”中,有两个字符 ‘a’,我们假设第一个为 a1,第二个为 a2。在程序匹配过程中,如果 j 指针指向 a2 时匹配失败,那么此时,主串中的 i 指针不动,j 指针指向 a1 ,很明显,由于 a1==a2,而 a2!=S[i],所以 a1 也肯定不等于 S[i]。

为了避免不必要的判断,需要对 next 数组进行精简,对于“abcac”这个模式串来说,由于 T[4] == T[next[4]] ,所以,可以将next数组改为:

模式串T:a b c a c

next  :0 1 1 0 2

这样简化,如果匹配过程中由于 a2 匹配失败,那么也不用再判断 a1 是否匹配,因为肯定不可能,所以直接绕过 a1,进行下一步。

实现代码:

void Next(char*T,int *next){

int i=1;

next[1]=0;

int j=0;

while (i

if (j==0||T[i-1]==T[j-1]) {

i++;

j++;

if (T[i-1]!=T[j-1]) {

next[i]=j;

}

else{

next[i]=next[j];

}

}else{

j=next[j];

}

}

}

使用精简过后的 next 数组在解决例如模式串为“aaaaaaab”这类的问题上,会减少很多不必要的判断次数,提高了KMP算法的效率。

例如:精简前为 next1,精简后为 next2:

模式串:a a a a a a a b

next1:0 1 2 3 4 5 6 7

next2:0 0 0 0 0 0 0 7

总结

KMP 算法,之所以比

字符串匹配代码C语言,KMP算法(快速模式匹配算法)详解以及C语言实现相关推荐

  1. 字符串:3.KMP算法(快速模式匹配算法)详解

    BF算法的改进--KMP算法(快速模式匹配算法). 串的普通模式匹配算法,大体思路是:模式串从主串的第一个字符开始匹配,每匹配失败,主串中记录匹配进度的指针 i 都要进行 i-j+1 的回退操作(这个 ...

  2. KMP算法next数组详解

    ==> 学习汇总(持续更新) ==> 从零搭建后端基础设施系列(一)-- 背景介绍 KMP算法的核心就是利用已匹配的信息来指导模式串的匹配.这里的已匹配信息叫做部分匹配表,也叫做next数 ...

  3. 字符串匹配—BF算法和KMP算法

    BF算法 本章重点是是KMP算法,但是由于其较难理解,先从更简单朴素的BF算法开始. 其思路非常简单 也就是,对这样两个字符串(称短的为模式串,长的为主串): 让主串和模式串的每个字符逐个匹配,如果从 ...

  4. 字符串匹配BF/RK/BM/KMP算法

    主串长度m,匹配串(模式串)长度n. 一.BF 强制算法比较,最容易想到的,时间复杂度O(m*n) 二.RK 计算出匹配串的哈希值,遍历主串,求每次对应位置相同长度的子串哈希值,比较两者是否相同. 若 ...

  5. 选择排序算法与示例详解(c语言)

    选择排序是排序算法的一种,思想就是,每一轮寻找数组中最大的值或者最小的值,放在头部或者放入一个新的数组.这样经历一轮遍历,数组或者新数组就是排好序的,他的目的很明确,每次找最大值或者最小值. 这个放在 ...

  6. 计算机网络反码求和代码,IP校验与算法反码求和详解

    [IT168 技术文档]一.校验和算法 之前一直只知道IP校验和算法反码求和相关的,但具体细节不清楚,今天了解了下. IP校验和主要是用来保证数据(IP包头)的完整性的.它用的算法非常简单,就是反码求 ...

  7. 变量的三要素是java_C语言变量三要素知识详解,C语言变量三要素没那么难

    1.变量的三要素 "变量的值"保存在内存的某个地方,如同使用门牌号确定地址一样,在内存中也给变量分配门牌号.在C的内存世界中,门牌号被称为变量的地址.即从变量中取值就是通过变量名找 ...

  8. 约瑟夫环数据结构c语言程序,约瑟夫环问题详解(C语言链表实现)

    约瑟夫环问题,是一个经典的循环 出列顺序依次为: 编号为 3 的人开始数 1,然后 4 数 2,所以 4 先出列: 4 出列后,从 5 开始数 1,1 数 2,所以 1 出列: 1 出列后,从 2 开 ...

  9. c语言实现sha1算法注解,【密码学】SHA1算法实现及详解

    1 SHA1算法简介 安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digit ...

最新文章

  1. 【小白的CFD之旅】20 计算区域的构建
  2. Java Web学习总结(6)——通过Servlet生成验证码图片
  3. XGBoost对比RandomForest、GBDT、决策树、SVM,XGB+LR精度还能提升
  4. MySQL笔记汇总---狂神说
  5. 股票历史数据下载接口踩坑记录
  6. python编程(python开发的三种运行模式)
  7. cmd合并多个ts文件,ffmpeg快速转ts为mp4文件,通过m3u8合并文件
  8. 清除1188.com
  9. 使用Docker部署前端项目实战教程,该踩的坑我都帮你踩了!
  10. URI与URL的区别
  11. #9733;色盲悖论正解!
  12. 《红孩儿引擎内功心法修练与Cocos2d-x》之结点系统(场景,层,精灵)
  13. Moonstarter IDO 一步一步的操作
  14. XEQ玻尿酸敏感肌可以用吗?效果怎么样?
  15. 【无锡学院/南信大滨江学院】IOS快捷指令登陆校园网——Iphone12实测
  16. nu.xom:Attribute
  17. H5页面input输入框,在ios手机中被顶出页面解决方案
  18. 比较两组数据的差异用什么图更直观_用好这11种可视化图表,数据可视化技能秒提升...
  19. MER:综述高通量测序应用于病原体和害虫诊断
  20. 微信公众平台的基础对接

热门文章

  1. 对3D显示技术的初步分类
  2. PTA 地下迷宫探索(图的遍历dfs)
  3. 组成原理——指令格式(地址码)
  4. RC滤波器与LC滤波器的比较
  5. 【python基础】正则表达式总结
  6. 用C/C++进行随机分组
  7. 4月19日第壹简报,星期三,农历闰二月廿九
  8. 存储程序通用计算机设计方案,第1章计算机设计方案基本原理.doc
  9. 不会还有人拿着手机拍屏幕翻译英语吧,pycharm翻译插件推荐
  10. PCA 主成分分析 (sklearn PCA)