【代码随想录训练营】【Day09】第四章|字符串|28. 实现 strStr()|459.重复的子字符串|字符串总结|双指针回顾

KMP算法

有关KMP算法的理论基础可查阅,有图文表示和较为详细的解释:《代码随想录》KMP算法


28. 实现 strStr()

实现 strStr()的关键在于利用KMP算法的解题思想,求出模版字符串的前缀表;那么什么是前缀表呢?

  • 前缀表是一个数组,常称为next数组或prefix数组。
  • 前缀表中存储的是该字符串在每一个字符之前的字符串中,最长相等前后缀的长度
  • 下标i之前(包括i)的字符串中,相等的前缀后缀的最大长度,同时亦是已匹配的最大长度的下标位置
  • 遍历完模版字符串后,即可得到模版字符串的前缀表

注意:获取Next数组,即获取前缀表的过程,只与模版字符串相关,不需要目标字符串的参与。

得到模版字符串的前缀表后,我们就可以利用它来与目标字符串进行匹配:

  • 按顺序比较模版字符串与目标字符串的每个字符
  • 当遇到不同的字符时,通过前缀表来回退指针,可以快速定位到已匹配的最大长度的下标位置
    • 已经匹配过的字段就不需要再进行匹配了,所以直接从定位到的下标位置开始继续匹配即可
    • 因此利用前缀表可以大大提升匹配效率
  • 当遇到相同字符时,则同时移动指针,继续依次比较字符。
  • 当模版字符串遍历结束时,则说明目标字符串中包含模版字符串
    • 通过目标字符串的当前指针下标,减去模版字符串长度 + 1,就可以得到匹配字符串的起始位置
  • 反之如果目标字符串先遍历完,则说明字符串匹配失败,返回 -1

注意:匹配字符串的过程中,只是利用前缀表,并不需要对前缀表进行任何操作。

  • 当字符串被确定时,它的前缀表也会被确定,是具备唯一性的。
  • 所以通过模版字符串得到其前缀表后,可以利用模版字符串和它的前缀表来与任何的字符串进行匹配验证,并不需要重新获取。

Java实现(原始前缀表):

class Solution {

    // getNext:求KMP算法中的next数组,遍历字符串,保存当前下标的最长相等前后缀长度
    public int[] getNext(String str){
        char[] arr = str.toCharArray();
        int[] next = new int[str.length()];
        // 首字符的最长相等前后缀长度一定为0
        next[0] = 0;
        for(int i = 1, j = 0; i < next.length; i++){
            while(j > 0 && arr[i] != arr[j]){
                j = next[j - 1];
            }
            if(arr[i] == arr[j]){
                j++;
            }
            next[i] = j;
        }
        return next;
    }

    public int strStr(String haystack, String needle) {
        int[] next = getNext(needle);
        char[] s = haystack.toCharArray(), t = needle.toCharArray();
        for(int i = 0, j = 0; i < s.length; i++){
            while(j > 0 && s[i] != t[j]){
                j = next[j - 1];
            }
            if(s[i] == t[j]){
                j++;
            }
            if(j == t.length){
                return (i - t.length + 1);
            }
        }
        return -1;
    }
}

原始前缀表 和 前缀表统一减一 的思路是异曲同工的,只是实现的过程不同:

  • 原始前缀表的next数组下标从0开始
  • 前缀表统一减一后的next数组下标从-1开始

Java实现(前缀表统一减一):

class Solution {

    // getNext:求KMP算法中的next数组,遍历字符串,保存当前下标的最长相等前后缀长度
    public int[] getNext(String str){
        char[] arr = str.toCharArray();
        int[] next = new int[str.length()];
        // 首字符的最长相等前后缀长度一定为0,统一减1后,下标从-1开始
        next[0] = -1;
        for(int i = 1, j = -1; i < next.length; i++){
            while(j >= 0 && arr[i] != arr[j + 1]){
                j = next[j];
            }
            if(arr[i] == arr[j + 1]){
                j++;
            }
            next[i] = j;
        }
        return next;
    }

    public int strStr(String haystack, String needle) {
        int[] next = getNext(needle);
        char[] s = haystack.toCharArray(), t = needle.toCharArray();
        for(int i = 0, j = -1; i < s.length; i++){
            while(j >= 0 && s[i] != t[j + 1]){
                j = next[j];
            }
            if(s[i] == t[j + 1]){
                j++;
            }
            if(j == t.length - 1){
                return (i - t.length + 1);
            }
        }
        return -1;
    }
}

459.重复的子字符串


字符串总结


双指针回顾


本来以为字符串的题目应该都是非常简单的,但是从未接触过的KMP算法再次把我从幻想中拉了出来,实在是高深莫测呀!

山重水复疑无路,柳暗花明又一村。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值