代码搬运工之面试常撕KMP

kmp,多k几次就好了。(结合简单例子比较容易理解)

熟读力扣300题,不会写题也会敲。
首先说一说,最长公共前后缀
比如abc,前缀不包含最后一个字符的所有以第一个字符开头的连续子串,前缀a,ab;
同理后缀,c,bc;
下面就是字符串的next数组,对应的值就是公共前后缀长度。
eg:abcabc
前缀:a,ab,abc,abca,abcab
后缀:c,bc,abc,cabc,bcabc
所以最长公共前后缀的长度是3。
在这里插入图片描述
上面是手动推出来的3,那用代码怎么写呢
j:保存最长公共前后缀的长度
i:当前字符串的下标

s.charAt(i)==s.charAt(j)

第一种,如果当前字符和j下标字符相同,可以通过前一个推导出来,
即next[i]=next[i-1]+1;又因为next[i-1]=j;所以next[i]=j+1;
有种dp的思想。
=在这里插入图片描述

   while (s.charAt(i)!=s.charAt(j) && j>0){
                j=next[j-1];
            }

第二种,这种情况下,j的下标要变为j前一个的next数组值,也就是回退。回退到哪里呢?因为当前s.charAt(i)!=s.charAt(j),其实想找到s.charAt(i)==s.charAt(j),这样就可以递推了。所以j要回退,也就是j=next[j-1],如果s.charAt(i)!=s.charAt(j),继续回退,直到j=0;
下面这个例子:
在这里插入图片描述
此时不相等,j要往前回退
在这里插入图片描述
这时s.charAt(i)==s.charAt(j),又可以用第一种的思路了。

求next数组的完整代码

public static void next(int []next,String s){
        next[0]=0;
        int j=0;
        for(int i=1;i<s.length();i++){
            while (s.charAt(i)!=s.charAt(j) && j>0){
                j=next[j-1];
            }
            if(s.charAt(i)==s.charAt(j))
                j++;
            next[i]=j;
        }
    }

对应的流程:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
next数组求出来,怎么进行字符串匹配呢
leetcode28. 实现 strStr()
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
其实kmp的思想就是,主串的下标不回退,只回退模式串,这就是是不同于暴力的方法。
eg: 此时i和j不匹配,如果是暴力的情况下,i要回退到b的位置

在这里插入图片描述
如果是用next数组,只需要把j移动到next[j-1]的位置,再进行比较
在这里插入图片描述
此时i和j还不匹配,继续回退
在这里插入图片描述
j回退到0,i往后移动
在这里插入图片描述
最终完成匹配。
在这里插入图片描述
28. 实现 strStr()

 public static int strStr(String ss, String pp){
        if(ss.length()==0||pp.length()==0)
            return -1;
        int []next = new int[pp.length()];
        next(next,pp);
        for(int i=0,j=0;i<ss.length();i++){
            while(j>0&&ss.charAt(i)!=pp.charAt(j))
                j=next[j-1];
            if(ss.charAt(i)==pp.charAt(j))
                j++;
            if(j==pp.length())
                return i-j+1;
        }
        return -1;

    }
    //abcabcabcd  (j=0,i=1开始,j是最长公共前缀和的长度)
    public static void next(int []next,String s){
        next[0]=0;
        int j=0;
        for(int i=1;i<s.length();i++){
            while (s.charAt(i)!=s.charAt(j) && j>0){
                j=next[j-1];
            }
            if(s.charAt(i)==s.charAt(j))
                j++;
            next[i]=j;
        }                                                                                                                                                              
    }
  1. 重复的子字符串
    给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成
    输入: s = “abab”
    输出: true
    解释: 可由子串 “ab” 重复两次构成
    这个题不是很容易想到kmp
    举个例子:
    在这里插入图片描述
    在这里插入图片描述
    可以清楚的看到包含重复的字符串,
    字符串的长度=next[最后一个字符下标]+重复字符串的长度
    有点意思。。
    上菜。。。。
public static boolean repeatedSubstringPattern(String s){
     if(s.length()==1)
         return true;
        int []next =new int[s.length()];
        getnext(s,next);
        //还要判断一下最后一个next数组的值,如果为0,肯定不包含重复字符串
     if(s.length()%(s.length()-next[s.length()-1])==0 && next[s.length()-1]!=0)
         return true;
     return false;
    }
    public static void getnext(String s , int []next){
        int j = 0;//保存最长前后缀长度
        next[0]=0;
        for(int i = 1;i<s.length();i++){
            while (s.charAt(i)!=s.charAt(j) && j>0)
                j=next[j-1];
            if(s.charAt(i)==s.charAt(j))
                j++;
            next[i]=j;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值