KMP是三位大牛:D.E.Knuth、J.H.Morris和V.R.Pratt同时发现的。为了解决模式匹配问题,也即寻找模式串(子串)在主串中第一次出现的位置,若模式串在主串中不存在则返回-1。

简单的模式匹配算法(BF算法)

对于简单的模式匹配,也即暴力破解,我们的想法是:从左到右一个个匹配,如果这个过程中有某个字符不匹配,就跳回去,将模式串向右移动一位。

两个变量i和j分别记录主串和模式串的下标,我们只需要从左到右比较i指针指向的字符和j指针指向的字符是否一致。如果一致就都向后移动,如果不一致,如下图:

A和E不相等,那就把i指针移回第1位(假设下标从0开始),j移动到模式串的第0位,然后又重新开始这个步骤:

对此我们可以写出简单模式匹配的函数:

int BF(const char *str,const char *sub)
{int lenstr=strlen(str);int lensub=strlen(sub);int i=0,j=0; //i记录主串下标,j记录模式串下标。int k=i;while(i<lenstr&&j<lensub){if(str[i]==sub[j]){++i;++j;}else   //匹配失败{j=0;i=++k;   //i从主串的下一位置开始,k中记录上一次的起始位置}}if(j<lensub){return -1;   //匹配失败}else{return k;}
}

算法说明:我们假设主串长度为m,模式串长度为n,则简单模式匹配算法的时间复杂度是O(n*m)。

KMP算法

而KMP算法可以充分利用模式串的部分匹配信息,保持主串i指针不变(不需要回溯主串),通过修改模式串j指针(变动的是模式串下标),让模式串尽量移动到有效的位置,以减少比较次数。可以实现算法时间复杂度为O(m+n)

这里我们引入一个数组next[]。

定义:next[j]为模式串位置与主串位置i失配时,滑动模式串使其模式串位置为next[j]的字符与主串位置i的字符继续匹配。

那么当模式串中某一个字符与主串不匹配时,j指针要移动到哪?

如上图:

C和D不匹配了,我们要把j移动到哪?显然是下标为1位。为什么?因为前面有一个A相同啊。

如下图也是同样情况:

可以把j指针移动到下标为2位置,因为前面有两个字母是一样的:

至此我们可以大概看出一点端倪,当匹配失败时,j要移动的下一个位置k。存在着这样的性质:最前面的k个字符和j之前的最后k个字符是一样的。

next[j]的含义就是一个固定字符串(下标从0到j-1)的最长前缀和最长后缀相同的长度。

规定特殊情况next[0]=-1。

比如:abcjkdabc,那么这个数组的最长前缀和最长后缀相同必然是3(abc)。

cbcbc,最长前缀和最长后缀相同是3(cbc)。

abcbc,最长前缀和最长后缀相同是不存在的为0。

注意最长前缀:是说以第一个字符开始,但是不包含最后一个字符。

比如aaaa相同的最长前缀和最长后缀是3(aaa)。

给定一个模式串,我们可以计算数组next[],在进行模式串匹配过程中哪里失配直接查找next[]数组对应的值即可。

如:

下标号    0 1 2 3 4 5 6 7 8 9

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

next[]值 -1 0 0 1 2 3 4 5 6 7

现在我们需要写出计算next[]的函数了。

这里我们借鉴数学归纳法的三个步骤(或者说是动态规划?):

1、初始状态

2、假设第j位以及第j位之前的我们都填完了

3、推论第j+1位该怎么填

接下来,我们开始用上面得到的条件来推导如果第j+1位失配时,我们应该填写next[j+1]为多少?

next[j+1]即是找模式串中从0到j这个子串的最大前后缀:

#:(#:在这里是个标记,后面会用)我们已知A1 == A2,那么A1和A2分别往后增加一个字符后是否还相等呢?我们得分情况讨论:

(1)如果str[k] == str[j],很明显,我们的next[j+1]就直接等于k+1

  用代码来写就是next[++j] = ++k;

(2)如果str[k] != str[j],那么我们只能从已知的,除了A1,A2之外,最长的B1,B3这个前后缀来做文章了。

那么B1和B3分别往后增加一个字符后是否还相等呢?

由于next[k] == 绿色色块所在的索引,我们先让k = next[k],把k挪到绿色色块的位置,这样我们就可以递归调用"#:"标记处的逻辑了。

由于j+1位之前的next数组我们都是假设已经求出来了的,因此,上面这个递归总会结束,从而得到next[j+1]的值。

另外有个特殊情况是k为-1时,不能继续递归了,此时next[j+1]应该等于0,即把j回退到首位。

即 next[j+1] = 0; 也可以写成next[++j] = ++k;

接下来,只欠初始条件了:

next[0] = -1, next[1]=0

k = 0, j = 1

至此,可以写出求next[]数组的函数:

void GetNext(const char *sub,int *next)
{int lensub=strlen(sub);/*初始条件*/next[0]=-1;next[1]=0;int j=1;int k=0;/*根据已知的前j位推测第j+1位*/while((j+1)<lensub){if(k==-1||(sub[j]==sub[k])){next[++j]=++k;}else{k=next[k];}}
}

KMP函数:

int KMP(const char *str,const char *sub)
{int lenstr=strlen(str);int lensub=strlen(sub);int i=0,j=0;int *next=(int *)malloc(lensub*sizeof(int));GetNext(sub,next);while(i<lenstr&&j<lensub){if(j==-1||(str[i]==sub[j])){++i;++j;}else{j=next[j];}}free(next);if(j<lensub) //匹配失败{return -1;}else{return i-lensub;}
}

测试代码:

int main()
{char str[]="ababacdabcdababc";char sub[]="ababc";int n=KMP(str,sub);printf("%d\n",n);return 0;
}

模式串匹配的BF算法和KMP算法相关推荐

  1. Algorithm:C++语言实现之字符串相关算法(字符串的循环左移、字符串的全排列、带有同个字符的全排列、串匹配问题的BF算法和KMP算法)

    Algorithm:C++语言实现之字符串相关算法(字符串的循环左移.字符串的全排列.带有同个字符的全排列.串匹配问题的BF算法和KMP算法) 目录 一.字符串的算法 1.字符串的循环左移 2.字符串 ...

  2. 若S作主串,P作模式串,试分别写出利用BF算法和KMP算法的匹配过程。

    目   录 题目: 百度文库-答案: (1) (2) MOOC标准答案: (1) (2) mooc答案-截图: 数据结构(C语言版)-严蔚敏2007 题目: 设字符串S='aabaabaabaac', ...

  3. BF算法和KMP算法

    给定两个字符串S和T,在主串S中查找子串T的过程称为串匹配(string matching,也称模式匹配),T称为模式.这里将介绍处理串匹配问题的两种算法,BF算法和KMP算法. BF算法 (暴力匹配 ...

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

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

  5. 【算法篇-字符串匹配算法】BF算法和KMP算法

    目录 前言 1. BF算法 1.1 画图分析 1.3 BF 算法的时间复杂度 2. KMP 算法 2.1 KMP 算法和 BF 算法 的区别 2.1.1 为什么主串不回退? 2. 2 next 数组 ...

  6. 数据结构之字符串匹配算法(BF算法和KMP算法)

    字符串匹配算法: 就是给定两个串,主串(s)和子串(sub), 查找子串是否在主串里面,如果找到便返回子串在主串中第一个元素的位置下标,否贼返回-1,. 在这里我 们讨论的时候主要用字符串来举例实现. ...

  7. 串的BF算法和KMP算法个人总结

    子串(模式串)的定位操作通常称作串的模式匹配 其中包含最初始的BF算法(Brute-Force)即简单匹配算法或者称作朴素的模式匹配算法,利用穷举法的思路 另一种就是改进后的KMP算法,还有对KMP算 ...

  8. BF算法和KMP算法实现

    https://blog.csdn.net/xxibgdrgn_liqian_a_/article/details/80690593

  9. 数据结构与算法之美笔记——基础篇(下):图、字符串匹配算法(BF 算法和 RK 算法、BM 算法和 KMP 算法 、Trie 树和 AC 自动机)

    图 如何存储微博.微信等社交网络中的好友关系?图.实际上,涉及图的算法有很多,也非常复杂,比如图的搜索.最短路径.最小生成树.二分图等等.我们今天聚焦在图存储这一方面,后面会分好几节来依次讲解图相关的 ...

最新文章

  1. AndroidStudio 新建不同的Drawable文件夹
  2. szu 寒训 day#3 ST表 和 LCA问题 附例题 菜鸡解法
  3. 在linux中加用户,Ubuntu使用教程——在Ubuntu中添加用户
  4. 【NLP】Github标星7.7k+:常见NLP模型的PyTorch代码实现
  5. Windows 7 Problem Steps Record工具
  6. 内存heap_哪个内存更快?Heap或ByteBuffer或Direct?
  7. mysql安装mac 压缩包_MAC mysql安装及设置
  8. (转)RabbitMQ学习之安装
  9. 十分钟读懂 黑客如何入侵Windows 操作系统
  10. 企业微信一天最多可以加多少人? 企业微信加好友功能会被限制吗?
  11. 无线信道仿真 matlab,基于Matlab的无线信道仿真.doc
  12. yy神曲url解析php_单文件PHP版视频解析源码(中间件)
  13. 隐藏文件的查看(Win/Linux/macOS)
  14. 安装selenium时报错,Unable to create process using ‘D:\ProgramData\python.exe D:\ProgramData\Scripts\pip-s
  15. 关于微信投票活动存在微信人工刷票数的情况解析
  16. 2021.9.15 每日总结
  17. uniapp使用plus.sqlite实现图片、视频缓存到手机本地
  18. 故意伤害罪具体会有什么处罚
  19. Json和List互相转化
  20. 免费的DNS服务OpenDNS、Google Public DNS

热门文章

  1. 免费讲座 再出江湖!
  2. 转载《XAMPP安装和使用教程》(转)
  3. python 根据网址获取股票
  4. 【note】Java程序设计基础第五版(上)
  5. java-将xlsx(excel)文件转换成json
  6. Eclipse 启动项目错误:class not found
  7. Understanding Linux CPU Load - when should you be worried?
  8. 从“跳一跳”来看微信小程序的未来
  9. SQL语句执行效率及分析
  10. 删除linux系统中的eth0.bak与多余的网卡