训练营0929|字符串KMP算法

 1.参考视频

KMP算法(易懂版)

代码随想录-KMP算法

KMP算法讲解--含next数组递推求解

代码求解next数组讲解

 左程云---KMP算法(后半部分1:40:00左右开始)

 文章-彻底理解KMP算法

2.理论部分

如果暴力BF算法,回溯的重复度过高,算法复杂度O(m*n),应用剪枝,而KMP就是一种剪枝方法。

 例如图中在匹配到此处不匹配时,看前面已经匹配过的子串的最长公共前后缀(最长相等前后缀前后缀不可以是字符串本身

前缀(不包含整串)后缀(不包含整串)相等?
ab×
abab
abbbab×
abbabbab×

所以最长公共前后缀是ab,所以第一个a移动到下一个a位置。

而且子串的公共前后缀看其本身就ok,完全不需要去看待匹配的串。所以引入了next数组的概念。

 


 然后对于KMP算法来说时间复杂度为O(n),n为主串的长度

匹配时通过next数组来决定,移给当前指针的下标是子串中哪个下标

例如,A和C此处无法匹配,而前一个next数组值为2,(ABAB的最长相等前后缀为AB,长度为2),所以下标2的A移到当前指针下面去

扫描指针只可前进不可回溯。

 

 3.递推求解next数组

---->有的解析说next 数组相当于“最大长度值” 整体向右移动一位,然后初始值赋为-1

---->有的解析说next数组相当于“最大长度值”,初始值为0

.......

其实就是一个怎么移的区别。

反正i(主串里面)一直与j(子串里面)匹配,i永远向前,j在往前跳

比如这里的子串ABABC

 

ABABC
-10012
(默认都是-1)A的最长公共前后缀长度i-1处与前缀处不相等i-1处与前缀处相等i-1处与前缀处相等

前缀处指的是前一个next数组里面的值所对应的下标处。next数组里面是最长公共前后缀的长度

对应的下标就已经不含有公共前后缀了,是公共前后缀后面一个数。下标索引从0开始。

next数组的递推求解---->i处的next数组的值

1.char2[i-1]的值去跟char2[next[i-1]]比较,(之前提到的前缀处)

2.相等的话,next[i]=next[i-1]++;

3.不相等的话,char2[i-1]去跟char2[next[i-1]]的前缀处比较,依次类推

 就是说,一直往前去比只要这边的next数组还有值就往前去找

 4.KMP过程

在已知next数组的前提下,i1遍历主串,i2遍历子串,i2可以向前回溯。

1.i1和i2对应的字符相等,i1++;i2++;

2.不相等并且next[i2]=-1;就是子串第一个字符,往前无可前的时候,i1++;

3.不相等的其他情况,i2往next[i2]处跳,i2=next[i2]

最后对比i2走到最后的话,返回i1-i2(第一个出现的位置)

 5.KMP完整代码

class Solution {
    /**
     * KMP算法,在haystack里面找出needle是否出现,返回出现的下标
     *
     * @param haystack
     * @param needle
     * @return
     */
    public int strStr(String haystack, String needle) {
        int len1 = haystack.length(), len2 = needle.length();
        if (len1 < len2) {
            return -1;
        }
        char[] ch1 = haystack.toCharArray();
        char[] ch2 = needle.toCharArray();
        int[] next = getNext(ch2);
        int i = 0, j = 0;
        while (i < len1 && j < len2) {
            if (ch1[i] == ch2[j]) {
                i++;
                j++;
            } else if (next[j] == -1) {
                i++;
            } else {
                j = next[j];
            }
        }
        return j == len2 ? i - j : -1;
    }

    public int[] getNext(char[] ch) {
        if (ch.length == 1) {
            return new int[]{-1};
        }
        int[] next = new int[ch.length];
        next[0] = -1;
        next[1] = 0;
        int i = 2;
        int cn = 0;
        while (i < ch.length) {
            if (ch[i - 1] == ch[cn]) {
                next[i++] = ++cn;
            } else if (cn > 0) {//不相等的话并且cn有值
                cn = next[cn];
            } else {
                next[i++] = 0;//cn=0或者为-1
            }
        }
        return next;
    }
}

6.总结

KMP还是磨了蛮久的,其实说实话写到现在说很懂也不能算,不过大概过程分为两部分,next数组求解,和整体求解。

此间有最长公共前后缀来构建next数组,而next数组的解法可以动态规划优化,并且代码里面我采用的是cn标注去参考的前缀处地方,有的参考代码直接省去了这个步骤直接用的j。

包括整体求解的时候,i不动,j往前动,动到next数组的下标。

之后复习的时候还得再多轮几遍

2022.10.08补

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值