#笔记整理
若不了解串的定义,可至:
串(string)的定义与表示 查看

串的模式匹配算法

求子串位置的定位函数 Index(S, P, pos)
求子串的定位操作通常称作串的模式匹配(其中子串P称为模式串)。

算法1:朴素模式匹配算法/简单匹配算法(Brute-Force算法,简称BF算法)

从目标主串s=“s1s2…sn”s=“s_1s_2…s_n”s=“s1​s2​…sn​”的第一个字符开始和模式串p=“p1p2…pm”p=“p_1p_2…p_m”p=“p1​p2​…pm​”中的第一个字符比较,若相等,则继续逐个比较后续字符;否则从目标主串s的第二个字符开始重新与模式串p的第一个字符进行比较。依次类推,若从目标串s的第i个字符开始,每个字符依次和模式串p中的对应字符相等,则匹配成功,该算法返回i;否则,匹配失败,函数返回0。

实现代码:

// ——————————串的定长顺序存储表示————————————————
#define MAXSTRLEN 255     //最大串长
typedef char SString[MAXSTRLEN+1];// C语言实现
int index(SString s, SString t, int pos){// t非空, 1 <= pos <= strlen(s)int i = pos;int j = 0;int sLen = strlen(s);int tLen = strlen(t);while(i < sLen && j < tLen){if(s[i] == t[j]){i++;j++;}else{i = i - j + 2;j = 1;}}if(j >= tLen){return i - tLen;}else{return 0;}
}// C++实现
int index2(string s, string t, int pos){// t非空, 1 <= pos <= Strlength(s)int i = pos;int j = 0;int sLen = s.length();int tLen = t.length();while(i < sLen && j < tLen){ // s[0]、t[0]为串长度if(s[i] == t[j]){i++;j++;}else{i = i - j + 2;j = 1;}}if(j >= tLen){return i - tLen;}else{return 0;}
}

源代码:github地址(其中有详细注释)

朴素模式匹配算法的时间复杂度分析
主串长n; 子串长m。可能匹配成功的位置(1 ~ n-m+1)。

  • 最好的情况下,
    第i个位置匹配成功,比较了(i - 1 + m)次,平均比较次数:
    最好情况下算法的平均时间复杂度O(n+m)。
  • 最坏的情况下,
    第i个位置匹配成功,比较了(i * m)次,平均比较次数:
    设 n >> m,最坏情况下的平均时间复杂度为O(n * m)。

算法2:朴素模式匹配算法的改进算法:KMP算法

KMP算法是 D.E.Knuth、J.H.Morris 和 V.R.Pratt 共同提出的,简称KMP算法。该算法较BF算法有较大改进,主要是消除了主串指针的回溯,从而使算法效率有了某种程度的提高。
当主串的第 i 个字符与子串的第 j 个字符失配时,若主串的第 i 个字符前的 ( k - 1 ) 个字符与子串的前 ( k -1 ) 个字符匹配,则只需主串的第 i 个字符与子串的第 k 个字符开始向后比较即可,i 不必回溯。

为此,定义 next[] 数组,表明当模式中第 j 个字符与主串中相应字符“失配”时,在模式串中需重新和主串中该字符进行比较的字符的位置 k,即next[j]=knext[j] = knext[j]=k。
实现代码:

 // KMP,C语言实现
int indexKMP(SString s, SString t, int pos){int i = pos;int j = 0;int sLen = strlen(s);int tLen = strlen(t);int next[tLen] = {0};getNext(next, t, pos); // 求next数组//getNextImprov(next, t, pos); // 使用改进的算法求next数组,根据需要选择for(int x=0; x<tLen; x++){cout << next[x] << endl;}while(i < sLen && j < tLen){if(j < 0 || s[i] == t[j]){ // 若匹配,或t已移出最左侧i++;j++;}else{j = next[j]; // 和BF算法的区别在此}}if(j >= tLen){return i - tLen;}else{return 0;}
}

next[]数组的生成与主串无关,只和模式子串自身有关。

以下为next函数的定义与求法(注意:此处的主串和模式子串都是从下标0开始的,与书上的不一样)

求next[]数组算法步骤:

  1. 初始next[0]=−1next[0] = -1next[0]=−1 表明主串从下一字符si+1s_{i+1}si+1​起和模式串重新开始匹配。因此i=i+1;j=0;i = i+1; j = 0;i=i+1;j=0;
  2. 设next[j]=knext[j] = knext[j]=k,求next[j+1]next[j+1]next[j+1] :
     ● 若 pk=pjp_k=p_jpk​=pj​ ,即p[j]=p[next[j]]p[j] = p[next[j]]p[j]=p[next[j]],则有“p1…pk−1pk”=“pj−k+1…pj−1pj”“p_1…p_{k-1}p_k”=“p_{j-k+1}…p_{j-1}p_j”“p1​…pk−1​pk​”=“pj−k+1​…pj−1​pj​” ,且不存在k′&gt;kk'&gt;kk′>k满足该等式,因此next[j+1]=k+1=next[j]+1next[j+1] = k+1 = next[j]+1next[j+1]=k+1=next[j]+1。
     ● 若 pk≠pjp_k ≠ p_jpk​̸​=pj​,则令 p[j]p[j]p[j] 和 p[next[next[j]]]p[next[next[j]]]p[next[next[j]]] 比较;
       ○ 若相等,则 next[j+1]=next[next[j]]+1next[j+1] = next[next[j]]+1next[j+1]=next[next[j]]+1;
       ○若不等,则沿失败链继续查找,直到某个 p[next[...next[j]...]]==p[j]p[next[...next[j]...]] == p[j]p[next[...next[j]...]]==p[j],或 next[...next[j]...]==−1next[...next[j]...]==-1next[...next[j]...]==−1,这时都置 next[j+1]=next[...next[j]...]+1next[j+1] = next[...next[j]...]+1next[j+1]=next[...next[j]...]+1。

实现代码:

// 生成next数组,C语言实现
void getNext(int next[], SString t, int pos){int i = pos;int j = -1;int tLen = strlen(t);next[0] = -1;while( i < tLen-1){if( j < 0 || t[i] == t[j]){ // 初始或匹配i++;j++;next[i] = j;}else{j = next[j];}}
}

改进 求 next[] 数组算法步骤的思路:
上述求 next 数组的步骤在模式串中相同的子串较多的情况下存在着缺陷。
如主串 ′aaabaaaab′'aaabaaaab'′aaabaaaab′ 在和模式子串 ′aaaab′'aaaab'′aaaab′ 匹配时,当 i=3、j=3i=3、j=3i=3、j=3 时,′a′!=′b′'a' != 'b'′a′!=′b′,如下图,由 KMP 算法可知,由 next[j]next[j]next[j] 的指示,接下来还会进行 i=3、j=2,i=3、j=1,i=3,j=0i=3、j=2, i=3、j=1,i=3,j=0i=3、j=2,i=3、j=1,i=3,j=0 这3次比较。实际上,因为模式中第0、1、2个字符和第3个字符都相等,因此不需要再和主串中第3个字符相比较,可以将模式一气向右移动4个字符的位置直接进行 i=4、j=0i=4、j=0i=4、j=0 时的字符比较。
也就是说,在之前求next方法的基础上,需要再加上一个判断:
p[j]?=p[k]p[j] ?= p[k]p[j]?=p[k],若相等,则 next[j]=next[k]next[j] = next[k]next[j]=next[k],否则,next[j]=knext[j] = knext[j]=k。

再举两个生成next数组的例子:

下面的例子包含了求next[j]的过程,理解该过程时暂不需要看"改进next[j]"那一行。

改进next[j]方法的计算过程由你们自己理解和验算吧。
实现代码:

// 生成next数组的改进算法,若模式串中相同的子串较多,可以使用此方法,可提高匹配效率。C语言实现
void getNextImprov(int next[], SString t, int pos ){int i = pos;int j = -1;int tLen = strlen(t);next[0] = -1;while( i < tLen-1){if( j < 0 || t[i] == t[j]){ // 初始或匹配i++;j++;if(t[i] != t[j]){  //改进之处next[i] = j;}else{next[i] = next[j];}}else{j = next[j];}}
}
KMP算法的时间复杂度

设主串s的长度为n,模式串t长度为m,在KMP算法中求next数组的时间复杂度为O(m),在后面的匹配中因主串s的下标不减即不回溯,比较次数可记为n,所以KMP算法总的时间复杂度为O(n+m)。

由于篇幅的关系,c++实现的代码请参考:
源代码:github地址(其中有详细注释)

部分内容来源:

  1. 《数据结构(C语言版)》----严蔚敏
  2. 《数据结构》课堂教学ppt ---- 刘立芳
  3. 《数据结构算法与解析(STL版)》 ---- 高一凡

【数据结构】字符串 模式匹配算法的理解与实现 Brute Force算法(BF算法)与KMP算法 (C与C++分别实现)相关推荐

  1. 数据结构——基于字符串模式匹配算法的病毒感染检测

    实验四 基于字符串模式匹配算法的病毒感染检测 [实验目的] 1.掌握字符串的顺序存储表示方法. 2.掌握字符串模式匹配BF算法和KMP算法的实现. [实验内容] 问题描述 医学研究者最近发现了某些新病 ...

  2. Problem C: 算法4-6:KMP字符串模式匹配算法实现

    Problem Description KMP算法是字符串模式匹配算法中较为高效的算法之一,其在某次子串匹配母串失败时并未回溯母串的指针而是将子串的指针移动到相应的位置.严蔚敏老师的书中详细描述了KM ...

  3. 数据据结构实验4《基于字符串模式匹配算法的病毒感染检测》

    (visual studio 2019可运行) 输入及输出要求见<数据结构C语言(第二版)>严蔚敏版 [本文仅用于啥都看不懂还想交作业选手] //KMP求解基于字符串模式匹配算法的病毒感染 ...

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

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

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

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

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

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

  7. 【数据结构与算法】字符串匹配 KMP 算法

    单模式串匹配 BF 算法和 RK 算法 BM 算法和 KMP 算法 多模式串匹配算法 Trie 树和 AC 自动机 KMP 算法 KMP 算法是根据三位作者(D.E.Knuth,J.H.Morris ...

  8. KMP算法--字符串模式匹配算法

    今天看到第四章<串>了,其中我觉得花的时间多一点的值得我写篇随笔的知识点就是:4.3 串的模式匹配算法:书上介绍了两种字符串匹配算法,一种是最简单最容易想到的逐个字符匹配算法(时间复杂度在 ...

  9. KMP字符串模式匹配算法【精简代码模板】

    什么是模式匹配 模式匹配(Pattern Matching)就是在一篇长度为n的文本S中,找某个长度为m的关键字P. KMP算法如何来的? KMP算法是由朴素字符串匹配算法优化而来,就是重新利用了朴素 ...

最新文章

  1. 【禅模式】如何进入极度专注的心流状态,让前端开发能力发挥到最大?设置VSCode禅模式快捷键Alt+F切换全屏,适合演示代码使用
  2. 不会MySQL索引,面试官让回家等通知!
  3. 【BZOJ1934】善意的投票(网络流)
  4. 如何建立java ssm工程_如何搭建一个ssm项目
  5. php两个数组找公共部分,PHP开发中如何查找两个数组的交集
  6. linux下无权限安装ffmpeg-4.1.3
  7. Swift 枚举简单使用
  8. Android星座查询实验报告,数字基带传输实验实验报告
  9. usleep头文件_Linunx的sleep,usleep,select,nonasleep对比与应用
  10. 1、pthon中类包含的方法
  11. 个性化的CRM具备的功能有哪些
  12. JetBrain Clion下载安装及配置环境
  13. iapp进度条倒计时_App启动页倒计时功能
  14. Ubuntu 学习笔记之——下载神器aria2
  15. 如何彻底删除keil5
  16. OpenCV—用meanshift和Camshift进行目标追踪
  17. 定义了Circle圆形类,在此基础上派生出Cylinder圆柱体类。 Circle圆形类定义如下: class Circle { public: Circle(double r):radius(r
  18. HDMI Type A、B、C、D接口图
  19. 恢复 outlook 邮箱 垃圾桶里删除的邮件
  20. 【面试_01】IBM 校招面试

热门文章

  1. 后羿采集器怎么导出数据_后羿采集器如何采集亚马逊评论
  2. 氟化硼二吡咯(BODIIPY) FL炔烃/氨基/羧基/NHS脂/叠氮/酰胫/马来酰亚胺/四嗪/DBCO
  3. python爬取当当图片和信息
  4. ubuntu 20.04 安装录屏软件 OBS 及卸载
  5. Polyspace应用:Bug Finder检测代码漏洞
  6. 免费的Kindle电子书资源
  7. Canvas制作简易涂鸦板
  8. Java使用ffmpeg进行音频格式转换
  9. 多重背包的二进制拆分法
  10. python+opencv通过颜色阙值识别黑色飞机,并且输出中心点