2021-07-22 粗略学习一下KMP -- LeetCode 28

KMP: 参考 https://www.zhihu.com/question/21923021
核心思想:跳过不可能成功的字符串比较,利用了Next数组

1. 构建next数组
精髓:快速求Next数组(最长公共前后缀数组)
next数组的完整定义:
定义 “k-前缀” 为一个字符串的前 k 个字符;“k-后缀” 为一个字符串的后 k 个字符。 k 必须小于字符串长度。
next[x] 定义为: P[0]~P[x] 这一段字符串,使得k-前缀恰等于k-后缀的最大的k.
快速构建next数组,是KMP算法的精髓所在,核心思想是“P自己与自己做匹配”。
采用递推的方式求出next数组。如果next[0], next[1], ... next[x-1]均已知,那么如何求出 next[x] 呢?

如果P[x] = P[next[x-1]] 则 next[x] = next[x-1] + 1;

如果P[x] != next[x-1] 则
abcbaabcba|b
01234|56789|10
A前缀|B后缀 | x
next[9] = 5 (abcba) 但是 P[10] != next[9]
观察一下,P[x]的k-前缀,一定满足A-前缀的前缀 = B-后缀的后缀+x, 而A = B,所以就是求A自己的最长公共前后缀,
即next[next[x-1]] ==> next[5] ==> 1, 此时P[10] = b = P[1], 所以next[10] = 1 (如果还不相等,就继续缩短,求next[1]...)

2.根据next数组移动标尺进行匹配


LeetCode 28:

/**
* 28-Implement strStr()-KMP-Easy
*
* 时间复杂度:O(n+m),其中 n 是字符串 haystack 的长度,m 是字符串needle 的长度。我们至多需要遍历两字符串一次。
*
* 空间复杂度:O(m),其中 m 是字符串 needle 的长度。我们只需要保存字符串 needle 的前缀函数。
*
*/

class Solution {
    public int strStr(String haystack, String needle) {
        if (needle.length() == 0) return 0;
        int[] nextArray = buildNextArray(needle);
        int i = 0; // index for haystack
        int j = 0; // index for needle 
        while (i < haystack.length()) {
            if (haystack.charAt(i) == needle.charAt(j)) {
                i++;
                j++;
            } else if (j != 0) { 
                // 根据next数组移动标尺
                j = nextArray[j - 1];
            } else {
                // 失配了,标尺右移
                i++;
            }

            if (j == needle.length()) {
                // 匹配成功
                return i - j;
            }
        }
        return -1;
    }


    private int[] buildNextArray(String needle) {
        int n = needle.length();
        int[] next = new int[n];
        next[0] = 0; // base case
        int i = 1;
        int current = 0;

        while (i < n) {
            if (needle.charAt(i) == needle.charAt(current)) {
                current++;
                next[i] = current;
                i++;
            } else if (current != 0) {
                current = next[current - 1];
            } else {
                next[i] = current;
                i++;
            }
        }

        return next;
    }
}
posted @ 2021-07-22 14:57  胡糊糊同学  阅读(77)  评论(0)    收藏  举报