kmp字符串匹配算法(数字也可)

暴力算法是不可取的,在此不做演示。

我们来看看 KMP 算法的实现细节。

先举个🌰:

原字符串和匹配串如下:

KMP 算法的效果
逐个判断是否匹配,到不匹配时停下来:

开始回退操作:

判断完成:

看起来就是简简单单的三个步骤,我们可以看出,其实 KMP 算法的关键,就在回退这个步骤。

如何利用前缀表(next 数组)实现回退操作
可见在回退过程中我们使用到了 next 数组,那么 next 数组存放的是什么呢?

next 数组存放的是当前长度下的 最长相同前后缀 的长度
以 abcabf举例

a时,最长前后缀长度是 0

因为是缀。总长度就只有 1的话单独一个字母不算做缀

字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串;后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。

ab时,很显然长度也是 0

abc时,很显然长度也是 0

abca时, 最长相同前后缀长度就是 1了,是 a

abcab时, 最长相同前后缀长度就是 2了,是 ab

abcabf时,没有 最长相同前后缀了,长度是0

tips
Le和 eL可是不能算相同前后缀的

深入了解 next 数组的运用
可以看到,现在已经匹配到 c与 f,出现了第一个不相等的字母了


怎么回退呢?答案是找到上一个

因为上一个肯定是已经匹配完成的字母,我们找到它对应的 next 数组值来指导我们进行下一步操作

那么问题来了,现在 next 数组值 2代表什么呢?它想让我们干什么呢?

那么问题来了,现在 next 数组值 2代表什么呢?它想让我们干什么呢?

那么问题来了,现在 next 数组值 2代表什么呢?它想让我们干什么呢?

2表示当前已经匹配的子字符串 abcab的最长相同前后缀长度是 2,而我们数组下标是从 0️⃣开始的,如果我们按照 2的指示,正好可以跳转到下标 2 的位置(也就是字母C),开始下一次匹配。

前后缀起到了什么作用,怎么能确定 C 前面的字母已经匹配呢?


前缀的应用场景是匹配串;
而后缀的应用场景是原字符串;
!!!!next 数组记录了我们的最长相同前后缀,在这里派上用场!!!!

​ 我们也就确定了 C 前面的字母已经匹配,可以放心的匹配之后的字母了

前面的abc也就没有作用,不会再被考虑,被丢弃掉了

next 数组代码实现细节图

 for (int right = 1, left = 0; right < needleLength; right++) {
//            定义好两个指针right与left
//            在for循环中初始化指针right为1,left=0,开始计算next数组,right始终在left指针的后面
            while (left > 0 && needle.charAt(left) != needle.charAt(right)) {
//                如果不相等就让left指针回退,到0时就停止回退
                left = next[left - 1];//进行回退操作;
            }
            if (needle.charAt(left) == needle.charAt(right)) {
                left++;
            }
            next[right] = left;
// 这是从 1 开始的

        }
// 循环结束的时候,next数组就已经计算完毕了
left 开始放置在 下标0位置,right 放置在下标 1位置,因为 next 数组下标0位置肯定是 0,默认已经填充好了。

比较 left 和 right,发现不匹配,放入 0

右移 right,比较 left 和 right,发现不匹配,放入 0

右移 right,left 和 right 已经开始匹配,放入 1

右移 right,left 和 right 继续匹配成功,放入 2

右移 right,left 和 right 匹配失败,放入 0

代码实现

    class Solution {
    public int strStr(String haystack, String needle) {
//自己敲一下子 前缀表不需要减一的实现方式
        int needleLength = needle.length();
        if (needleLength == 0) return 0;
//        当needle是空字符串时,返回0

        int[] next = new int[needleLength];
//        定义好next数组
        for (int right = 1, left = 0; right < needleLength; right++) {
//            定义好两个指针right与left
//            在for循环中初始化指针right为1,left=0,开始计算next数组,right始终在left指针的后面
            while (left > 0 && needle.charAt(left) != needle.charAt(right)) {
//                如果不相等就让left指针回退,到0时就停止回退
                left = next[left - 1];//进行回退操作;
            }
            if (needle.charAt(left) == needle.charAt(right)) {
                left++;
            }
            next[right] = left;
// 这是从 1 开始的

        }
// 循环结束的时候,next数组就已经计算完毕了


        for (int i = 0,j=0; i <haystack.length() ; i++) {

            while (j>0&&haystack.charAt(i)!=needle.charAt(j)){
                j=next[j-1];
            }
            if (haystack.charAt(i)==needle.charAt(j)){
                j++;
            }
            if (j==needleLength) return i-needleLength+1;
        }
        return -1;


    }
}


作者:seventeenth
链接:https://leetcode-cn.com/problems/implement-strstr/solution/duo-tu-yu-jing-xiang-jie-kmp-suan-fa-by-w3c9c/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值