KMP算法中最大公共前后缀长度的理解(逐步分析)

阅读: 评论:0

KMP算法中最大公共前后缀长度的理解(逐步分析)

KMP算法中最大公共前后缀长度的理解(逐步分析)

一、前言

        笔者在拜读了网上众多KMP算法后,仍对部分细节存疑,前前后后花了两天时间去消化KMP算法的知识,又历时三天抽出时间整理得出本文。

        个人认为,网上在讲KMP时并没有仔细探究最大公共前后缀长度的含义与求法,而是跳步到求next数组、nextval数组等等。虽然现在的我知道本质上是完全一样的,但是对于初学者来说着实不是很友好…

        因此本文着重分析如何计算最大公共前后缀长度,在弄懂完这个之后再去理解next和KMPsearch就会觉得相对轻松。

        若对你有帮助,欢迎点赞收藏~;若有问题,恳请诸位大佬在评论区指出,定会立即改正!


二、最大公共前后缀长度的理解

        1. 一个串的前后缀:

        前缀是指以串第一个字符开头且不包含最后一个元素的连续的子串,后缀是指以串最后一个字符结尾且不包含第一个元素的连续的子串(可以为串自身)。

        eg.假设设串为ABCDEFGH,则前缀有A,AB,ABC等,后缀有H,GH,FGH等

        2. 一个串的公共前后缀:

        顾名思义,如果前缀集合与后缀集合中存在一个相同的元素,即存在一个子串,它既可以是前缀也可以是后缀,则称这个子串为该串的公共前后缀。

        eg.假设串为AB_CSDN_AB,则公共前后缀有AB

        3. 最大公共前后缀长度:

        根据2中的分析我们可以得出一个串的公共前后缀,然而公共前后缀不一定只有一个,因此,需要我们寻找最长的公共前后缀,并得到它的长度,称为最大公共前后缀长度。

        eg.假设串为ABA_CSDN_ABA,则公共前后缀有A,AB,即最大公共前后缀长度=3 (ABA)

        4. 为什么要找最大公共前后缀长度:(逐步分析)

        假设文本串是ABA_ABAY_CD,模式串是ABA_ABAX_CD

        当我们匹配到文本串的Y,发现与模式串的X不匹配的时候,一般的暴力做法是:令模式串指回头部,令文本串指回原先的头部+1,再重新开始比较

        首先我们要理解,匹配的本质是:刷新文本串的某一个位置(可以是一个字符,也可以是一个子串)来作为头部(该头部与模式串的头部相同),直到满足该头部接下来的元素与模式串头部接下来的元素相同

        暴力做法是一个字符一个字符地作为头部来遍历文本串

        但实际上,我们可以一个子串一个子串(这个子串也可以是一个字符)地作为头部来遍历文本串,这就是KMPSearch

        在模式串的X位置之前的子串ABA_ABA中,我们可以找到最大公共前后缀ABA,它对应的长度是k=3,则代表模式串X位置之前的k个元素和模式串的前k个字符是完全相同的

        (这里读者要留意一下“模式串X位置之前k个元素”和“模式串的前k个元素”的两个“前”的差别,相信你们可以get到)

        而既然已经匹配到Y了,是不是说明文本串Y位置之前的k个元素 == 模式串X位置之前的k个元素 == 模式串的前k个元素

        这说明什么?

        相当于我们是不是已经找到了一个新的头部,这个头部不就是文本串Y位置之前的k个元素构成的子串,它正好对应模式串的头部

        因此,按照先前讲的匹配的本质,我们只需要匹配头部之后的元素,即文本串从Y开始,亦即文本串的头指针不动,模式串从第k个元素开始,即模式串的头指针右移k位,然后再一个个字符判断

        之后,我们只要重复上述思想,不断移动模式串的头指针,但保持文本串的头指针不动,即可实现KMP算法。因此我们需要找到模式串每一个位置的最大公共前后缀长度


三、计算最大公共前后缀的长度

        1. 声明和初始化

        令模式串为char p[length],声明int类型数组changdu[length]来存储不同位置对应的最大公共前后缀长度

        声明两个变量i和k,i用来遍历模式串p,k用来存储最大公共前后缀长度

        初始化changdu[0] = 0,因为只有一个字符时,一定没有公共前后缀,因此从i = 1,k=0开始遍历模式串p

        每一次循环,刚开始的k是上一个位置对应的最大公共前后缀长度,而第k个位置即是公共前缀的下一个元素

        2. p[i] = p[k]

        若新添加的元素p[j]等于当前最大公共前缀的后一个元素即p[k],相当于此时新的后缀与新的前缀又相同了,所以最大公共前后缀多了一个元素,长度加1,即changdu[i] = ++k

        3. p[i] != p[k]

        若新添加的元素p[j]不等于当前最大公共前缀的后一个元素即p[k],则原先的公共前后缀被破坏,不再成立

        所以,此时需要找到是否存在一个更短的公共前后缀,令原先的公共前后缀为子串s,而新的更短的公共前后缀实际上就是:s+p[i]的公共前后缀,即“公共前后缀的公共前后缀”

        因此,更新k值为s的最大公共前后缀长度,即第k-1个位置的最大公共前后缀长度 = changdu[k-1] 

        4.赋值

        因此既然每一次都是寻找公共前后缀,我们只需要不断递归上述步骤

        当遇到了p[i] = p[k]的情况,才把当前的k值赋给changdu[i]

        当k==0 || k==1时,不需要再继续递归,因为此时可以断定不存在公共前后缀,所以把k=0赋给changdu[i]


四、代码

        1. GetChangdu函数

/*
1.k是当前子串的最大公共长度
2.changdu[i]存储的是当前子串0~i所对应的最大公共长度值k 
综上,p[k]则是原本最大公共前后缀中的前缀的后一个元素
*/void GetChangdu(char* p,int changdu[],int length)
{int len = length;changdu[0] = 0;			//只有一个字符即p[0]时,最大公共长度当然为0int i = 1,k = 0;		//因此我们从p[1]开始遍历,并初始化最大公共长度为0 while (i < length){//若新添加的元素p[j]等于当前最大公共前缀的后一个元素p[k]//则相当于此时新的后缀与新的前缀又相同了 //因此直接将当前的k+1赋给changdu[i]即可 if (p[i] == p[k]){changdu[i] = ++k;i++; }//当k值不断缩小为公共前后缀的最大公共长度直到缩小到为1//或者不存在公共前后缀时 //代表此时的串没有公共前后缀,故令changdu[i] = k = 0即可 else if (k == 0){changdu[i] = k;i++; }//若新添加的元素p[j]不等于当前最大公共前缀的后一个元素p[k],则新的后缀与新的前缀不同了 //此时需要找到是否存在一个更短的公共前后缀//相当于是寻找(公共前后缀+新元素)的公共前后缀 //更新k值为最大公共前后缀的最大公共长度即changdu[k-1],而不再是当前串的最大公共长度 //重复上述流程,就可以找到changdu[i] elsek = changdu[k-1];}   
}

        2. GetNext函数:

        因为进行KMPSearch时,进行到模式串p的位置i时,向右移动的距离实际上是changdu[i-1],因此我们只需要将原来的changdu数组右移,并赋值changdu[0] = -1即可

        这就是网上大多方法实现next的基本思想

        (其实不难得出,可以将GetChangdu和GetNext合二为一,具体的可以参考下面给出的链接,本文只专注于解析GetChangdu)

void GetNext(int *next,int *changdu,int length){int temp[length];for (int i=0;i<length;i++)temp[i] = changdu[i];for (int i=1;i<length;i++)next[i] = temp[i-1];next[0] = -1;
}

        3. KMPSearch

int KMPSearch(char* maistr,char* substr,int length1,int length2,int* next){int i=0,j=0;while (i<length1 && j < length2)if (j == -1 || maistr[i] == substr[j]){i++;j++;} elsej = next[j];if (j == length2)return i-length2;else return -1;
}

五、参考文献

从头到尾彻底理解KMP(2014年8月22日版)_kmp算法 csdn-CSDN博客

本文发布于:2024-01-31 09:36:41,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170666500227574.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

上一篇:【OpenCV
标签:后缀   算法   长度   KMP
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23