KMP算法C语言实现详解

KMP算法C语言实现详解

封面6 拷贝.png

作者:老九—技术大黍

社交:CSDN

公众号:老九学堂(新手有福利)

特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者授权

前言

在讲解算法时我先参考了O'Reilly出版的《Mastering Algorithms with C》、Wiki网站和严蔚敏的《数据结构(C语言版)》教材。因为,我们希望通过多方面专业的参考,希望学习算法时是非常严谨的。

KMP简介

我没有在《Mastering Algorithms with C》一书找到KMP的定义,我觉得维基上的解释是标准的,所以截图如下:

image-20210322104047331.png

关于这个算法的解释网上有很多,我在这里就不参考翻译,也不再累述了。我们列出这个维基解释处,还列出一个针对KMP算法的网站应用解释:brilliant.org/wiki/knuth-… 大家可以自行参考理解,希望可以帮助大家多方面的认知该KMP的准确定义。

下面我们的介绍C语言实现过程。

KMP算法C语言实现

思路:

字符串匹配是计算机的基本任务之一。举个“栗子”:有一个字符串"aaaaaacaaacac",我想知道,里面是否包含另一个字符串"aaaac"?这里就会使用到串的模式匹配算法。串的模式匹配算法有很多种,最常见的是传统的BF算法KMP算法

BF算法的设计思想

  1. 主串和模式串逐个字符进行比较

image-20210322104820036.png

image-20210322104830492.png 2. 当出现字符不相同(失配)时,主串的比较位置重置为起始位置的下一个字符位置,模式串的比较位置重置为起始位置

image-20210322104910408.png

image-20210322104919341.png 3. 匹配成功返回主串中匹配串的起始位置,否则返回错误码

BF算法的设计缺陷

在BF算法中,每次失配都需要回溯指向上次比较起始字符的下一个字符!我们经过观察会发现:在回溯的时候,已匹配部分似乎有一部分没必要再比较了!这样就可以降低算法的时间复杂度

image-20210322105021977.png

BF算法的设计缺陷的解决方案

KMP(Knuth-Morris-Pratt)算法有效的解决了BF算法的缺陷。它以三个发明者命名,第一个字符K就是著名科学家Donald·Knuth

image-20210322105143399.png

但是这种算法不太容易理解,网上有很多解释,但读起来都很费劲。以下将给大家详细的介绍KMP算法的设计思想和工作原理。

KMP算法的设计思想

在匹配过程中出现字符比较不相等时,主串S已比较的位置不回溯,模式串T比较的位置进行移动。

image-20210322105226343.png

image-20210322105233178.png

image-20210322105239865.png

image-20210322105252778.png

image-20210322105300767.png

在匹配过程中有一个难题需要解决:如何计算模式串T在失配时的移动位数?经过三位牛人的观察思考,总结设计出部分匹配函数

部分匹配函数是KMP算法中最难以理解的部分。首先需要理解前缀后缀最大共有长度的概念。

前缀指除了最后一个字符以外,一个字符串的全部头部组合。

image-20210322105342213.png

后缀指除了第一个字符以外,一个字符串的全部尾部组合。

image-20210322105348521.png

最大共有长度(部分匹配值)指前缀和后缀中最大共有元素,没有则为0。例如“abab”的前缀为“a”、“ab”、“aba”,后缀为“b”、“ab”、“bab”,所以最大共有元素为“ab”,最大共有长度为2。

回顾一下KMP算法的匹配过程:

image-20210322105418144.png

红线框出的部分恰好就是失配时已匹配部分“aaaa”的最大共有元素“aaa”,这一部分字符也就是不需要再重复比较直接跳过的字符。

image-20210322105432854.png

在代码实现过程中,j移动后的位置=模式串T的起始下标+部分匹配值。通常起始下标为0,因此j移动后的位置=部分匹配值,即j=next[j],next[j]就是部分匹配函数,j为失配时的位置。

因此接下来就成了对部分匹配函数的实现。将“aaaac”以首字符起始的所有子串的最大共有长度枚举出来,构成部分匹配表,它描述了失配时的下标j与部分匹配值的关系。

image-20210322105456085.png

部分匹配表则是通过模式串T的自匹配实现:

image-20210322105506679.png

image-20210322105512408.png

image-20210322105519190.png

image-20210322105526080.png

image-20210322105532172.png

C语言代码实现

KMP匹配算法

int KMPCompare(HString parent, HString child, int pos){
    int next[255];
    Get_Next(child, next);
    int i = pos - 1;
    int j = 0;      //j用于子串child中的起始位置
    while(i < parent.length && j < child.length){
        if(j == 0 || parent.ch[i] == child.ch[j]){
            ++i;
            ++j;
        }else{
            j = next[j];    //i不变,j后退
        }
    }
    if(j == child.length){
		return (i + 1) - j;
    }
    return 0;
}

部分匹配函数的实现

void Get_Next(HString child, int * next){
    int i = 0;
    int j = -1;
    next[0] = -1;   //不会用到
    while(i < child.length){
        if(j == -1 || child.ch[i] == child.ch[j]){
            ++i;
            ++j;
            next[i] = j;
        }else{
            j = next[j];
        }
    }
}

main函数

void main(){
    /*使用KMP算法匹配串*/
    HString parent, child;
    StrAssign_HeapString(&parent, "BBC ABCDAB ABCDABCDABDE"); //字符串赋值函数略
    StrAssign_HeapString(&child, "ABCDABD");
    printf("Index = %d\n", KMPCompare(parent, child, 1));
}

最后

我们给出的代码实现肯定不是最好,这里只是抛砖引玉,希望给大家不一样的C语言的代码实现,如果有不足之处,希望大家指正和补充。

记得给大黍❤️关注+点赞+收藏+评论+转发❤️

作者:老九学堂—技术大黍

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值