KMP算法记录

记录一下kmp算法,以及对于求next数组代码的理解,方便日后复习,适合对kmp算法有一定了解的人食用

学会kmp就可以不使用暴力解法来解决掉这个简单题
28

先进行一些约定

  • 前缀:一个字符串中,包含首字母,但是不包含尾字母的子串
  • 后缀:一个字符串中,不包含首字母,但是包含尾字母的子串
  • s串:文本串
  • p串:模式串

为什么要使用kmp

  • 对于28题,如果暴力求解的话,每次遇到不匹配的情况,就需要退回到最初的起点进行重新匹配。但是当在某位置不匹配说明了在这个位置之前是都匹配上了,如果回到最初的起点,就没有利用到这个信息,因此三个大佬(K、M、P)搞出来这个算法,目的就是遇到不匹配的情况的时候,不用回退到最初的起点,而是退到合适的位置在开始。
  • 要想实现这个想法,就需要引出这个next数组
  • next数组与之前提到的前后缀息息相关,同时这个数组和p串紧密相连但是和s串毫无瓜葛
  • next数组长度和p串长度相等,它的作用是:当我们在进行匹配时,假设匹配到p串的第j个字母的时候不匹配的,这时候我们就去next数组里找next[j-1],它会告诉我们退回到那个地方重新开始
  • 那么next数组里存的到底是什么呢? 举个例子,next数组中下标为j中存放的是p串中,从开头到这个下标(包含)为止的子串中的最大公共前后缀的长度
  • 在细致点:p串为:abcdabce
  • 下标为6(对应的p串子串为abcdabc)
  • 这个子串的前缀有:a、ab、abc、abcd、abcda、abcdab
  • 这个子串的后缀有:c、bc、abc、dabc、cdabc、bcdabc
  • 很明显,最大公共前后缀的长度为3(abc),因此next[6] = 3

求解next数组代码分析

public void getNext(int[] next,String s){
    int j = 0;
    next[0] = 0;
    for(int i = 1; i < s.length(); i++){
        while(j > 0 && s.charAt(i) != s.charAt(j)){
            j = next[j-1];
        }
        if(s.charAt(i) == s.charAt(j)){
            j++;
        }
        next[i] = j;
    }
}
  • 分析如下:(看分析前,再熟悉一下前面的前后缀的概念)
  • 第一个字符作为p串的最小子串,自然是没有前后缀的,因此next[0] = 0(进入for前的初始化)
  • j = 0,i = 1,此时为p串的前两个字母构成的子串,若为ab,则最大公共前后缀的长度为0;若为aa,则最大公共前后缀的长度为1;在第二种情况下,若第三个字母为a,则最大公共前后缀的长度为2,若为其他,则j变为next[0] = 0。
  • 带着大家走了两步for循环,现在进行一下归纳:
  • 从最后next[i] = j这个赋值语句和next数组中的含义,我们可以直接推断出,j就是代表当前的最大公共前后缀的长度
  • 假设当前j = c(当前循环结束,马上开始新一次的循环),在开始判断前,意味着到目前为止,前面的最大公共前后缀的长度为c,也就是指针j之前的和指针i之前的相等的子串,如果指针i和指针j所指的字母相同,那么当前的最大公共前后缀的长度再增长一位,就是if语句中的意思。如果指针i和指针j所指的字母不相同,那我指针j就退回到最大公共前后缀的长度小1的位置上在和指针i的字母进行比较,知道相等或者指针j退回到开头即j = 0;
  • 如上就是构建next数组的解读,后续会补一个示意图
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值