关于KMP算法next数组的学习思考

前言

        这篇帖子是我个人在学习KMP算法获取next数组这部分代码时形成的思考,在浏览了诸多帖子以后,发现对于k = next[k]这行代码的逻辑还是没有清晰的认知,因此想自己写一篇帖子理顺自己的思路,主要是借此平台做学习笔记,如果可以帮助到别人那自然是极好的,也希望有大佬可以指教。

        这篇帖子不探讨KMP算法详细的实现原理,只关注通过模式串得到next数组的逻辑,特别是k = next[k] 这行代码的逻辑。

        关于KMP算法整体的讲解我参考的是这篇文章:很详尽KMP算法(厉害) - ZzUuOo666 - 博客园 (cnblogs.com)

int* get_next(string s) {
        int len = int(s.length());
        int *next = new int[len]();
        int j = 0, k = -1;
        next[0] = -1;
        while(j < len - 1) {
            if(k == -1 || s[j] == s[k]) {
                ++j; ++k;
                next[j] = k;
            } else {k = next[k];}
        }
        return next;
    }

一、next数组的基本意义

        next[j] = k所表示的含义是字符串中下标为j的字符之前(不包括该字符),能匹配的前缀子串以及后缀子串的最大长度是k,通俗来讲,就是当模式串匹配到下标为[j]的元素时如果失配了,可以从下标为[k]的元素开始进行比对,[k]之前的元素已经匹配了。

注意:以上思考是针对我在前言中所贴的代码而言的,不同的代码可能会有不同理解。

二、获取next数组值的基本逻辑

        对于长度大于1的匹配的前后缀,都是在已经匹配的前后缀的基础上,对前缀的后一位以及后缀的后一位进行比较,如果两个后一位的元素相同,则找到了新的匹配的前后缀。这一点和KMP算法的核心匹配逻辑是相同的(毕竟核心代码逻辑都一样,都是x = next[x])。

三、k = next[k],即指针回退的具体逻辑

        k = next[k]针对两种情况,一种是没有匹配的前缀和后缀,即s[k] 与 s[j]始终不相等,指针回退至“-1”状态,然后下一个循环符合if判断语句中“k == -1”的语句,“++k; ++j”,然后给next[j+1]赋值0,这种情况比较好理解,不做详细阐述,用图像模拟函数每个循环。

        该图像中前三行分别是字符串元素、字符串下标、下标对应next数组值,相同颜色代表相同的元素值,k与j是指向元素的指针,默认s[k] 与 s[j]始终不相等。

        更重要的一种情况是,目前已经匹配的前缀与后缀长度为next[j](也就是当前k的值),但是s[j] != s[k],所以要尝试找到更短的已经匹配的前缀与后缀,如果有更短的匹配的前缀与后缀,且该前缀的后一个元素与后缀的后一个元素相同那就找到了,那就将找到的前缀(后缀)长度赋值给next[j+1]。

        下面就针对这种情况做具体分析:

        首先通过图片模拟获取next数组时的一种情况,相同颜色代表相同的字符,方框内的深色数字即字符串下标,最下方的浅色数字代表下标对应的next数组的值,k与j是指针。

        上图的模拟的情况是,程序运行至k = 5,j = 12,s[k] != s[j],此时next[12]的值已经求出,要求next[13]的值。根据此时的条件,因为k != -1 && s[k] != s[j],所以程序会进入else条件语句,即运行k = next[k],具体运行逻辑我分几步进行梳理。

1、将数组分为两部分,一部分是已经匹配的前缀以及k所指的元素,另一部分是已经匹配的后缀以及j所指的元素。

        已经明确的是,next数组值的含义是某个元素之前(不包括该元素能匹配的前缀子串以及后缀子串的最大长度,所以此时next[k] = 3就是s[k]之前能匹配的前缀与后缀的长度为3,因为除了“k == -1”,只有当“s[k] == s[j]”时,程序才会执行“++i; ++j”,且找不到匹配的前后缀时k会回退至“-1”,所以s[k]之前的元素与s[j]之前的k个元素是相同的,这点通过观察图像也可以很清晰的知道,因此,假如s[k]之前(即s[0]至s[k-1])的字符串有可以匹配的前后缀,那s[j]之前的k个元素(即s[j-k]至s[j-1])形成的字符串必然有相同的前后缀

2、将找到的更短的符合条件的前缀与后缀分开表示

        运行了k = next[k]后,k的值变为3,此时k指向的就是找到的更短的已经匹配的前缀的后一个元素,前缀与后缀分开表示,这时候就不难发现,k与j所指的元素相同,因此就找到了新的符合条件的前后缀,即符合“s[k] == s[j]”。

3、将字符串按原来的样式表示

        此时已经执行if语句,k与j的值都已经加一,并且对next[j]赋值找到的符合条件的前后缀长度:4。

        至此,整个get_next函数执行完毕。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值