Day9(KMP难点)|28. 找出字符串中第一个匹配项的下标 459. 重复的子字符串 总结(字符串部分+双指针)

28. 找出字符串中第一个匹配项的下标

  • 题目链接28. 找出字符串中第一个匹配项的下标
  • 解题思路:第一想法肯定是两层for循环遍历,时间复杂度是O(m*n),这个题本身考的是KMP算法,对这块不太熟,头一次接触,看了两个讲解视频之后写了一种前缀表不变的解法,但是感觉有些还是没理解。
  • 主要难点:KMP算法 难
  • 解题步骤
    • 需要先定义一个函数用来求字符串的前缀表next数组,数组里放的是这个字符串每个位置上的最大前后缀相同长度,在这个函数中需要处理前后缀相等和前后缀不相等两种情况,当前后缀不相等的时候,j需要进行回退,回退的位置的当前j所在位置前一个位置对应的next数组值,当j已经回退到头时就不再回退了,而当前后缀相等的时候,填充next数组并把j向后移动
    • 在主函数中先定义一个和子串长度相同的数组,并调用上述函数生成这个子串的前缀表
    • 然后开始比较两个字符串,当出现不匹配的时候,去查这个位置前一个位置对应的前缀表,找他最长的前后缀相等值,然后以这个值为下标重新开始比对。
  • 解题时间:60+5
  • 代码
class Solution {
    public int strStr(String haystack, String needle) {
        if(needle.length()==0){
            return 0;
        }
        int[] next = new int[needle.length()];//新建next数组,长度与needle长度相同
        getNext(next,needle);//调用函数算needle的前缀表

        //用前缀表实现查询的过程:
        int j = 0;
        for(int i=0;i<haystack.length();i++){
            while(j>0 && needle.charAt(j) != haystack.charAt(i)){
                j = next[j-1]; //j回退
            }
            if(needle.charAt(j) == haystack.charAt(i)){
                j++;
            }
            if(j == needle.length()){
                return i-needle.length()+1;
            }
        }
        return -1;
    }
    //计算前缀表next数组的函数
    private 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(j) != s.charAt(i)){
                j = next[j-1];//回退到next数组上一位对应的下标位置
            }
            if(s.charAt(j) == s.charAt(i)){
                j++;
            }
            next[i]=j;
        }
    }
}

459. 重复的子字符串

  • 题目链接459. 重复的子字符串
  • 解题思路:可以用循环暴力求解,但是这个题也可以使用KMP算法来做,KMP算法针对的就是字符串子串问题,重复的子字符串也算是子串。因为 最长相等前后缀的规则,当一个字符串由重复子串组成的,最长相等前后缀不包含的子串就是最小重复子串。
  • 主要难点:KMP算法,怎么确定最小的循环?
  • 解题时间:30+5
  • 代码
class Solution {
    public boolean repeatedSubstringPattern(String s) {
        if (s.equals("")) return false;

        int len = s.length();
        s = " " + s;//加一个空格,从1开始
        char[] chars = s.toCharArray();
        int[] next = new int[len + 1];//前缀表

        // 构造 next 数组过程,j从0开始(空格),i从2开始
        for (int i = 2, j = 0; i <= len; i++) {
            // 匹配不成功,j回到前一位置 next 数组所对应的值
            while (j > 0 && chars[i] != chars[j + 1]) j = next[j];
            // 匹配成功,j往后移
            if (chars[i] == chars[j + 1]) j++;
            // 更新 next 数组的值
            next[i] = j;
        }

        // 最后判断是否是重复的子字符串,这里 next[len] 即代表next数组末尾的值
        if (next[len] > 0 && len % (len - next[len]) == 0) {
            return true;
        }
        return false;
    }
}

总结

  • 字符串总结
    • 库函数的使用:需要熟悉自己语言里一些库函数的实现和应用方法,但是也不应该过于依靠库函数,有的时候也需要自己实现库函数的功能,例如:split分割,reverse反转,substr子字符串等等
    • 双指针法的应用:有的时候一眼看上去的暴力揭发是两层循环嵌套的时候,或者需要新建额外空间实现的时候,可以想一下能不能用双指针方法减少一层循环,或是减少额外空间消耗做到原地实现。
    • KMP:KMP算法作为一种专门解决字符串子串问题的方法,在字符串部分用的比较多,整体思想可能比较难懂,需要后面二刷的时候重新看,不光是背模板,要理解每一步的思路。KMP算法的主要思想就是当字符串不匹配的时候,不再从头重新比,而是想办法用上之前已经查过的结果,减小比较次数。核心是前缀表的思路和设置,难点是找到最后解题需要什么
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值