字符串匹配算法: 就是给定两个串,主串(s)和子串(sub), 查找子串是否在主串里面,如果找到便返回子串在主串中第一个元素的位置下标,否贼返回-1,。 在这里我
们讨论的时候主要用字符串来举例实现。 总共有两个算法,分别为BF算法和它的优化算法KMP算法
首先我们来讲一下BF算法,BF 算 法 即 朴 素 算 法 :它 的 实 现 方 式 是 这 样 的 , 假 定 我 们 给 出 字 符串 ”ababcabcdabcde”作为主串, 然后给出子串: ”abcd”,现在我们需要查找子串是否在主串中出现, 出现返回主串中的第一个匹配的下标, 失败返回-1;

我们看上图,这是两个字符串,上面是主串,下面是子串,BF算法的基本原理用绿字写在旁边,大家可以动手画一下来体会这个过程。
那么,我用代码来实现一下BF算法函数:

int BF(Str *s,Str *sub,int pos)
{if(pos<0||pos>s->length){return -1;}int i = pos;//pos是传的参数,表示从主串的那个位置开始匹配int j = 0;int lens = s->length;int lensub = sub->length;while (i < lens && j < lensub)//进入while的条件是标记i和j都没有走完主串和子串{if (s->elem[i] == sub->elem[j])//字符相等{i++;j++;}else//字符不相等{i = i - j + 1;//让i回到开始位置加1的地方,继续匹配j = 0;//让j指向子串的0号下标}}if (j >= lensub)//这里做一个判断,j>=lensub的情况下就是在主串中找到了子串,就返回i-j,i是pos位置,减去j等于走了一个子串的长度。{return i - j;}else return -1;
}

其实它函数就这么一点,很简单,很好理解。BF函数有三个参数,分别是主串(s),子串(sub)和从那个位置开始查找的一个整型变量(pos)。注释很详细。

KMP 算法: 相对于 BF 算法来说 KMP 算法更为高效, 原因在于 BF 算法的时间复杂度是:
O(mn),M 代表主串的长度, n 代表子串的长度。 而 KMP 的话, 时间复杂度就变为 O(m+n);接下来我们看一下, 具体的实现过程, 还是一样, 我们举例来看: KMP 和 BF 唯一不一样的地方在,主串的 i 并不会回退到开始的位置加1的地方, 并且 j 也不会移动到 0 号位置。 我们先看个例子:

在这时,i并没有回退,但j退回到0.

KMP算法中标记量i,j的移动并不像BF算法那样,这里我们必须引进一个量K,K表示的是匹配失败时子串的回退下标,所有的K都存在一个next数组中,这个next数组说起来就是一个存下标的整型数组嘛,可计算起来就有点绕了。。。
记下来我们手动计算一下next数组的K值

上图是我求好next数组的一个字符串,接下来,我要用一段超级好理解的大白话来给你将K值的计算,首先,第一个元素的K值是-1,第二个元素的K值是0,这个是固定的,我们要从第三个元素开始,利用一二元素的K值来计算,先明确两点,1,计算哪个元素的K值要看前面的元素,不能包括自己,2,需找子字符串时必须以0号元素,也就是第一个元素开始,以要计算K值的那个元素的前一个元素结束,回到上图,第三个元素是c,我们只能在前两个字符的子串中找,而且要以a开始,b结束,很明显找不到两个一样的满足条件的真子串,所以2号下标元素的K值为0;3号下标元素为a,不包含自己前面有三个元素,真子串只能是a,b,c,ab,bc,很明显也找不出两个满足条件的真子串,所以3号下标的元素的K值为0;4号下标为b,前面有四个元素,以a元素开始,a元素结束,可以有的真子串是a,b,c,a,ab,bc,ca,abc,bca,可以看出只能是单个元素a瞒足条件,所以4号下标元素K值为1;5号下标元素是d,前面一共有五个字符元素,利用上面的方法很好找出满足条件的是子串ab,因为找出的子串长度为2,则5号下标元素的K值为2;6号下标元素为e,要找一个以a开始,d结尾的真子串,很明显没有,所以6号下标元素的K值为0,后面的7和8号下标元素的K值大家可以自己算算。
手动求K值就是这样一个方法,下面给大家两个例子,可以动手算算,答案在该博客末尾,计算完可以对比一下。
练习 1: 举例对于”abcababcabc”, 求其的 next 数组?
练习 2: 再对”abcabcabcabcdabcde”,求其的 next 数组?
明白了K值的定义和计算,那么我们现在进入到KMP算法最关键的地方,KMP算法的原理和公式推导

上图是我给出的一个例子,我们用j来做子串的标记,假设j++走到5号下标处不适配,我们可以算出5号下标元素的K值为2,所以表示紧挨者的j-1和j-2开始的0和1号下标的元素相同,所以在这里等于ab子串已经找过,5号下标d应该回退到2号下标,我们把3号下标设为k,把3号下标的元素设为Pk,跳出这个例子,next数组本身存放的就是j的回退路线下标K值,设0号下标元素为P0,
5号下标元素为Pj(因为此时j刚好调到5号下标元素处),那4号下标元素就是Pj-1。

由此可以轻松得到一个数学公式,即:P0 … Pk-1 = Px …Pj-1 因为前后找到的两个字符串本身就是相等的,所以在这样的顺序存储方式中,下标之差应该也是相等的,得到0 + k-1 = x + j-1 合并得x=j-k,所以上面等式可以写为P0 … Pk-1 = Pj-k … Pj-1。我们可以看出这个等式只是有关于下标j和k,那么作为Pk和Pj作为字符,它们比较只有两种结果,要么相等,要么不等,那么,我们先不放假设Pk = Pj,把Pk和Pj分别加到上面等式的两边可得,P0 … Pk = Pj-k … Pj(1),因为next数组本来就是存放K值的,所以next[i] = k(2),那么由(1)(2)两个等式可以找规律明显得出
next[j+1] = k+1,也就是说:

那么另一种Pk != Pj的情况呢?还是一样先来看一个例子:

当Pk!=Pj时,按相等的方法回退一次并不能满足,但是我们很容易发现,在回退一次的基础上再回退一次便可解决问题,所以这里很容易得出K = next[K];
原理和公式推导就讲到这里,如果你还是不清楚,建议你可以举例子来画着捋一捋思路。
接下来我用代码实现以下求next数组的算法

void GetNext(int* next,char* sub)
{int len = strlen(sub);next[0] = -1;next[1] = 0;int i = 2;int k = 0;while(i<len){if((k == -1) || sub[k] == sub[i -1])//if和else就是我上面所说的Pk = Pj和Pk!=Pj的情况,这里k=-1进入的原因是存在回退到0号下标元素的情况,因为它的K值为-1,所以需要执行一次k++,来让它置为0{next[i] == k+1;i++;k++;}else{k = next[k];}}
}

代码实现KMP算法函数:

int KMP(const char* s,char* sub,int pos)
{int lens = strlen(s);int lensub = strlen(sub);int i = pos;int j = 0;int* next = (int*)malloc(sizeof(int)*lensub);//malloc动态开辟内存,大小为sub串的长度乘sizeof取类型GetNext(next,sub);//通过上面写的GetNext函数自动获取next数组while(i < lens && j < lensub)//while循环的条件是标记值i和j分别没有走完主串和子串,如果主串走完说明没有匹配到,子串走完说明已经匹配到,这是都跳出while循环{if( (j == -1) || s[i] == sub[j]){i++;j++;}else{j = next[j];}}free(next);//这里一定要记得释放next动态开辟的内存,否则会发生内存泄漏的情况if(j >= lensub){return i - j;}elsereturn -1;
}

最后,完整代码加运行结果奉上:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
void GetNext(int* next,char* sub)
{int len = strlen(sub);next[0] = -1;next[1] = 0;int i = 2;int k = 0;while(i<len){if((k == -1) || sub[k] == sub[i -1]){next[i] = k+1;i++;k++;}else{k = next[k];}}
}int KMP(const char* s,char* sub,int pos)
{int lens = strlen(s);int lensub = strlen(sub);int i = pos;int j = 0;int* next = (int*)malloc(sizeof(int)*lensub);GetNext(next,sub);while(i < lens && j < lensub){if( (j == -1) || s[i] == sub[j]){i++;j++;}else{j = next[j];}}free(next);if(j >= lensub){return i - j;}elsereturn -1;
}int BF(char *s,char *sub,int pos)
{if(pos<0 || pos>strlen(s)){return -1;}int i = pos;int j = 0;int lens = strlen(s);int lensub = strlen(sub);while (i < lens && j < lensub){if (s[i] == sub[j]){i++;j++;}else{i = i - j + 1;j = 0;}}if (j >= lensub){return i - j;}else return -1;
}int main()
{char *s = "abefzkrbc";char *sub = "zkr";printf("BF   =%d\n", BF(s, sub, 0));printf("KMP   =%d\n", KMP(s,sub,0));return 0;
}

练习答案:第一题:-1 0 0 0 1 2 1 2 3 4 5
第二题:-1 0 0 0 1 2 3 4 5 6 7 8 9 0 1 2 3 0
这是我学习中的浅见,如果那里有问题,或者不全面,之后会继续补充,谢谢!

数据结构之字符串匹配算法(BF算法和KMP算法)相关推荐

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

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

  2. BF算法和KMP算法

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

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

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

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

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

  5. 模式串匹配的BF算法和KMP算法

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

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

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

  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. IIS7 上配置运行 Ruby CGI环境
  2. queueing 优化_简单聊聊网页的资源加载优化
  3. Linux常用测试命令
  4. 【编译原理】文法的基本概念
  5. 天池 在线编程 排名查询
  6. Nginx 的线程池与性能剖析【转载】
  7. 黑科技!当会爬虫的Python遇上会画图的FineBI……
  8. Tiny-Event-Manager 安全轻量的资源管理器
  9. 如何在Java中获取系统属性?
  10. Python破解wifi密码
  11. vmware 官方下载
  12. 数字孪生CIM智慧城市BIM,城市cim可视化解决方案公司
  13. BROTHER 废墨清零教学
  14. 单片机原理及应用第四版林立课后选择题
  15. 下载并安装JDK7 教程
  16. 程序员坐牢了,会被安排写代码吗?
  17. 点到线的距离计算公式
  18. MA5680T配置指导
  19. 字符集和字符编码(Charset Encoding)
  20. 工业相机概述-选型事项-生产厂家汇总

热门文章

  1. Android四大组件完全解析(一)---Activity
  2. 翻纸牌游戏 【HDU - 2209】【规律】
  3. 弹出页面代码及相关解释
  4. 蓝桥杯—第八届—A组—第二题—跳蚱蜢 {C语言}=====【可调试】
  5. DaVinci DM6437 中文教程
  6. 网络营销实战课-微信实操
  7. 使用 pandas 做数据可视化
  8. winform实现拖曳功能
  9. 微型计算机简单并行接口实验心得,实验二简单并行接口.doc
  10. android 获取短信消息,Android开发获取短信的内容并截取短信