子串(模式串)的定位操作通常称作串的模式匹配

其中包含最初始的BF算法(Brute-Force)即简单匹配算法或者称作朴素的模式匹配算法,利用穷举法的思路

另一种就是改进后的KMP算法,还有对KMP算法的一种优化算法

现在先展示第一种BF算法的代码及解决思路:

#define _CRT_SECURE_NO_WARNINGS 1
#define MAXSIZE 255
#define ERROR 0
#include<stdio.h>
#include<stdlib.h>typedef struct {char ch[MAXSIZE];int length;
}SString;int main()
{int compare(SString S, SString T, int pos);SString S;SString T;//定义主串和模式串(子串),分别为S,Tint i = 1;//i表示主串第一个元素的数组下标//比较串通常从数组下标为1开始,0位置不存储或者通常存放数组的长度//这里为了方便数组S.ch[0]和T.ch[0]不存放任何数据,长度事先定好并存在S.length和T.length中int j = 1;//j表示子串第一个元素的数组下标printf("请输入主串长度\n");scanf("%d%*c", &S.length);//使用%*c吃掉回车,不然scanf读取回车存在缓存区//下一次使用scanf输入char类型的元素会先读取缓存区的换行符(\n),影响多次scanf使用//这里有好几种解决方法,具体请参考《解决scanf读取回车问题》printf("请输入主串元素\n");while (i < S.length+1)//这里数组长度的定义是你输入的总元素的个数,不是所谓的总长度//最开始我们的0下标位置没有存放,所以i从1开始,循环S.length次,所以<S.length+1//这种比较不确定时候掰手指头带几个数字算算~~~~{scanf("%c", &S.ch[i]);//输入前面已经定好个数的主串元素,记住输入时候不要加空格!!!!!i++;}printf("请输入子串长度\n");//这里就不用考虑回车的问题了,%c情况下才会读取,%d无影响scanf("%d%*c", &T.length);//使用%*c吃掉回车,不然scanf读取回车存在缓存区,影响多次scanf使用printf("请输入子串元素\n");while (j < T.length + 1){scanf("%c", &T.ch[j]);j++;}int pos = 1;//pos是比较起点,也就是从主串哪一位置开始和子串比较int s = 0;//s保存比较函数的返回值printf("请输入比较起始点:\n");scanf("%d", &pos);if (pos<1 || pos>S.length)//记住比较最小从1开始,最大不能超过主串总长//因为我们把元素从1开始存放,所以从哪个元素开始位序和下标一样,不用担心不符合问题{exit(ERROR);}else{s = compare(S, T, pos);}if (s != 0)printf("字串从第%d位开始匹配成功",s);elseprintf("未匹配成功");return 0;
}int compare(SString S, SString T, int pos)//BF算法,i每次要回溯
{int i = pos;//i代表起时从哪个位置开始比较int j = 1;//j代子串的元素位置while (i <= S.length&& j <= T.length)//如果i,j分别小于等于各自长度就可以继续比,大于就没得比了~{if (S.ch[i] == T.ch[j])//如果相等{++i;++j;//i和j都++,都准备开始比较各自的下一个元素}else{i = i - j + 2;//i回溯到当前开始比较位置的下一位置//这里理解为(i-j+1)+1,前面括号里面是刚开始比较的i的位置序号,后面加一表示往后移动一位//从后面一位开始,其位置元素作为新的比较起点开始比较j = 1;//j回溯到起始位置,每次匹配失败后子串就从头开始比较}}if (j > T.length) //最后如果比较成功了,子串对应的j达到了最后一位,即T.length//但是要记住,你最后一个元素匹配成功进入的if语句还有一个++i和++j,所以最后j=T.length+1return (i - T.length);//上面说明了,i最后还有个++,最后i值为比完的最后一位的下一位对应的ielsereturn 0;
}

每次比较后,i都要回溯,j也得从第一位开始重新比较,效率其实非常低,于是一种新的算法腾空出现,就是最初的KMP算法。

他的核心思想就是i不回溯,一遍到底,j回溯到合适位置,不是每次从头开始

KMP的主函数和BF的差不多,先展示其匹配函数:

int KMP_cmp(SString S, SString T, int pos)//KMP算法
{int i = pos;int j = 1;int next[MAXSIZE];//定义next数组nextval(T, next);//为next数组赋值while (i <= S.length && j <= T.length){if (j==0||S.ch[i] == T.ch[j])//多增加了j==0的判断{++i;++j;}else{j = next[j];//i不回溯,j回溯到适当位置}}if (j > T.length)return (i - T.length);elsereturn 0;
}

再就是创建不完美的next数组的函数:

​
void nextval(SString T, int* next)//最原始的KMP算法的next数组
//如果碰到aaaaab这种子串,会被赋值012345,增加了不必要的回溯
{int i, k;i = 1;k = 0;next[1] = 0;while (i < T.length){if (k == 0 || T.ch[i] == T.ch[k]){++i;++k;next[i] = k;}elsek = next[k];}
}​

先给大家简单分析一下k和i的含义和关系(改进后的照样继续分析即可):

首先是next数组生成时候我们把if和else抽出来分析:

现在把最完美的KMP算法展示给大家:

(解决了前面重复元素的多次k回溯问题)

#define _CRT_SECURE_NO_WARNINGS 1
#define MAXSIZE 255
#define ERROR 0
#include<stdio.h>
#include<stdlib.h>typedef struct {char ch[MAXSIZE];int length;
}SString;int main()
{int KMP_cmp(SString S, SString T, int pos);SString S;SString T;//定义主串和模式串(子串)int i = 1;int j = 1;printf("请输入主串长度\n");scanf("%d%*c", &S.length);//使用%*c吃掉回车,不然scanf读取回车存在缓存区,影响多次scanf使用printf("请输入主串元素\n");while (i < S.length + 1){scanf("%c", &S.ch[i]);i++;}printf("请输入子串长度\n");//这里就不用考虑回车的问题了,%c情况下才会读取,%d无影响scanf("%d%*c", &T.length);//使用%*c吃掉回车,不然scanf读取回车存在缓存区,影响多次scanf使用printf("请输入子串元素\n");while (j < T.length + 1){scanf("%c", &T.ch[j]);j++;}int pos = 1;int s = 0;printf("请输入比较起始点:\n");scanf("%d", &pos);if (pos<1 || pos>S.length)//记住比较最小从1开始,最大不能超过主串总长//因为我们把元素从1开始存放,所以从哪个元素开始位序和下标一样,不用担心不符合问题{exit(ERROR);}else{s = KMP_cmp(S, T, pos);}if (s != 0)printf("字串从第%d位开始匹配成功", s);elseprintf("未匹配成功");return 0;
}//主函数和BF算法一致,关键在于一个next数组的构建,他存放着j要回溯到的位置void nextval(SString T, int *next)
{int i, k;i = 1;//i表示元素的位序k = 0;//k代表当前i位置元素的最大公共前缀+1//例如abcabd,next值为011123next[1] = 0;//第一个元素的next值人为先定义为0while (i < T.length){if (k == 0 || T.ch[i] == T.ch[k])//如果k==0或者i位置和k位置的元素值相等{++i;++k;if (T.ch[i] != T.ch[k])//如果i位置的值和k位置的值不等,也就是外面if中以k==0条件进来的next[i] = k;//把此时k的值赋给i位置元素的nextelsenext[i] = next[k];//如果相等i位置的next和k位置的next相等}elsek = next[k];//如果既不相等k又不等于0,k就被赋值为k位置的next}
}int KMP_cmp(SString S, SString T, int pos)//KMP算法
{int i = pos;int j = 1;int next[MAXSIZE];//定义next数组nextval(T, next);//为next数组赋值while (i <= S.length && j <= T.length){if (j==0||S.ch[i] == T.ch[j])//多增加了j==0的判断{++i;++j;}else{j = next[j];//i不回溯,j回溯到适当位置}}if (j > T.length)return (i - T.length);elsereturn 0;
}

下面是输入输出结果:

下面展示用数组第一位(下标为0)存储数组长度的相同算法(BF与KMP):

#define MAXSIZE 255
#define ERROR 0
#include<stdio.h>
#include<stdlib.h>typedef struct {char ch[MAXSIZE];
}SString;int main()
{//int compare(SString S, SString T, int pos);int KMP_cmp(SString S, SString T, int pos);SString    S;SString T;int i = 1;int j = 1;char x, y;//先把串元素的值赋给x,y再把其赋给数组对应下标位置,这样能方便终止输入printf("请输入主串元素(输入'#'停止,每输入一个元素用enter换行)\n");scanf("%c%*c", &x);for (i = 1; x != '#'; i++){S.ch[i] = x;scanf("%c%*c", &x);}S.ch[0] = i - 1;//数组下标为0的位置存放数组的长度fflush(stdin);//清空缓存区的所有字符,这里因为最后输入#后面接了一个换行,不清除后面子串第一个接收的就是换行符printf("请输入子串元素(输入'#'停止,每输入一个元素用enter换行)\n");scanf("%c%*c", &y);for (j = 1; y != '#'; j++){T.ch[j] = y;scanf("%c%*c", &y);}T.ch[0] = j - 1;int pos = 1;//pos是比较起点,也就是从主串哪一位置开始和子串比较int s = 0;//s保存比较函数的返回值printf("请输入比较起始点:\n");scanf("%d", &pos);if (pos<1 || pos>S.ch[0])//记住比较最小从1开始,最大不能超过主串总长//因为我们把元素从1开始存放,所以从哪个元素开始位序和下标一样,不用担心不符合问题{exit(ERROR);}else{//s = compare(S, T, pos);s = KMP_cmp(S, T, pos);}if (s != 0)printf("字串从第%d位开始匹配成功", s);elseprintf("未匹配成功");return 0;
}//int compare(SString S, SString T, int pos)//BF算法,i每次要回溯
//{
//  int i = pos;//i代表起时从哪个位置开始比较
//  int j = 1;//j代子串的元素位置
//  while (i <= S.ch[0] && j <= T.ch[0])//如果i,j分别小于等于各自长度就可以继续比,大于就没得比了~
//  {
//      if (S.ch[i] == T.ch[j])//如果相等
//      {
//          ++i;
//          ++j;//i和j都++,都准备开始比较各自的下一个元素
//      }
//      else
//      {
//          i = i - j + 2;//i回溯到当前开始比较位置的下一位置
//          //这里理解为(i-j+1)+1,前面括号里面是刚开始比较的i的位置序号,后面加一表示往后移动一位
//          //从后面一位开始,其位置元素作为新的比较起点开始比较
//          j = 1;//j回溯到起始位置,每次匹配失败后子串就从头开始比较
//      }
//  }
//  if (j > T.ch[0]) //最后如果比较成功了,子串对应的j达到了最后一位,即T.length
//      //但是要记住,你最后一个元素匹配成功进入的if语句还有一个++i和++j,所以最后j=T.length+1
//      return (i - T.ch[0]);//上面说明了,i最后还有个++,最后i值为比完的最后一位的下一位对应的i
//  else
//      return 0;
//}
//
void nextval(SString T, int *next)
{int i, k;i = 1;//i表示元素的位序k = 0;//k代表当前i位置元素的最大公共前缀+1//例如abcabd,next值为011123next[1] = 0;//第一个元素的next值人为先定义为0while (i < T.ch[0]){if (k == 0 || T.ch[i] == T.ch[k])//如果k==0或者i位置和k位置的元素值相等{++i;++k;if (T.ch[i] != T.ch[k])//如果i位置的值和k位置的值不等,也就是外面if中以k==0条件进来的next[i] = k;//把此时k的值赋给i位置元素的nextelsenext[i] = next[k];//如果相等i位置的next和k位置的next相等}elsek = next[k];//如果既不相等k又不等于0,k就被赋值为k位置的next}
}int KMP_cmp(SString S, SString T, int pos)//KMP算法
{int i = pos;int j = 1;int next[MAXSIZE];//定义next数组nextval(T, next);//为next数组赋值while (i <= S.ch[0] && j <= T.ch[0]){if (j==0||S.ch[i] == T.ch[j])//多增加了j==0的判断{++i;++j;}else{j = next[j];//i不回溯,j回溯到适当位置}}if (j > T.ch[0])return (i - T.ch[0]);elsereturn 0;
}

下面是输入输出结果:

时间复杂度:(m,n分别是主串和子串的长度)

BF算法: O(m*n)

KMP算法 O(m+n)

KMP的思想和next数组创建建议多独立分析,先搞清思想再来用代码实现~~

独立研究next数组的创建过程------为什么需要、为什么这么建立

串的BF算法和KMP算法个人总结相关推荐

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

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

  2. BF算法和KMP算法

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

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

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

  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算法)

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

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

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

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

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

  8. BF算法和KMP算法实现

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

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

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

最新文章

  1. 中山行书百年纪念版字体可以商用吗_干货|免费可商用字体
  2. 【移动通信】4G LTE帧结构
  3. GraphQL 学习
  4. 【django】项目准备
  5. 虚拟机上的ubuntu安装vmware tools
  6. 百度地图API开发指南
  7. Java笔记-spring-rabbitmq中使用@RabbitListener消费(手动确认,获header数据)
  8. python3 正则 去除 html标签、提取正文内容_Python通过正则表达式去除(过滤)HTML标签,提取文字...
  9. java 获取文件大小_利用百度AI OCR图片识别,Java实现PDF中的图片转换成文字
  10. 【简●解】POJ 1845 【Sumdiv】
  11. 密码学40年|重要历史与人物
  12. GD32Pack包下载地址
  13. My97DatePicker事件无效,触发事件calendar.js,datepicker.css等文件无法加载
  14. C++[POJ1849]铲雪车问题——树形DP求出树的直径
  15. 爬取20万数据,我们深扒了风口上的「享物说」
  16. 乐动手环app下载安装_乐动健康下载app_乐动健康下载安装app手环v2.34
  17. 苹果怎么换行打字_微信新功能上线!安卓苹果都有!网友:再也不怕被刷屏了!!...
  18. 渗透测试之---xss-labs闯关【1-14关】
  19. java点打印没反应_兄弟QL打印机SDK - 不打印,没有任何反应(QL-710W)
  20. el-radio-button 设置默认选中问题

热门文章

  1. mpalyer 编译错误解决办法
  2. java 主函数解释
  3. 六级作文小模板。。。
  4. Minecraft:随时随地创造你的世界
  5. 支付宝支付 - 电脑网站支付
  6. android自定义控件之可以表示画笔大小的调整画笔size 的SeekBar
  7. 覆盖率coverage
  8. 【MQ 快速入门】介绍、分类、组成、优缺点、测试点
  9. 网页中动态GIF图片不动的问题
  10. 三国志战略版鸿蒙梦魇,三国志战略版:陆抗版诸葛亮,低智队的梦魇!