代码随想录Day9 | 28. 找出字符串中第一个匹配项的下标 | KMP

97 阅读2分钟

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

题目链接:28. 找出字符串中第一个匹配项的下标

思路: 反复看了很久的视频。首先next是一个前缀表,模式串与前缀表对应位置的数字表示的就是:下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。 如何构建一个前缀表呢,用i代表后缀的末尾,j代表前缀的末尾,j从第一个字符前的空字符开始(这样j可以从0开始,不用从1开始),i从第二个字符开始,如果i位置和j + 1位置不想等,j进行回退,回退到next[j],如果相等,j++,next[i] = j。

有了next表之后我们开始匹配,如果s[i] == p[j + 1]那么j++,如果不想等,j回退到next[j]。当j的值等于模式串的长度时,说明存在,坐标为i - 模式串的长度。

我的代码:

class Solution {
    public int strStr(String haystack, String needle) {
        if (needle.isEmpty()) return 0;
        int n = haystack.length(), m = needle.length();
        haystack = " " + haystack;
        needle = " " + needle;

        char[] s = haystack.toCharArray();
        char[] p = needle.toCharArray();

        int[] next = new int[m + 1];
        getNext(next, p);
        
        // 匹配过程
        for (int i = 1, j = 0; i <= n; i++) {
            while (j > 0 && s[i] != p[j + 1]) j = next[j];
            if (s[i] == p[j + 1]) j++;
            if (j == m) return i - m;
        }
        return -1;

    }

    // 构建过程
    private void getNext(int[] next, char[] ch) {
        for (int i = 2, j = 0; i < next.length; i++) {
            while (j > 0 && ch[i] != ch[j + 1]) j = next[j];
            if (ch[i] == ch[j + 1]) j++;
            next[i] = j;
        }
    }
}

总结:

「KMP 匹配」过程: 首先匹配串会检查之前已经匹配成功的部分中里是否存在相同的「前缀」和「后缀」。如果存在,则跳转到「前缀」的下一个位置继续往下匹配。

跳转到下一匹配位置后,尝试匹配,发现两个指针的字符对不上,并且此时匹配串指针前面不存在相同的「前缀」和「后缀」,这时候只能回到匹配串的起始位置重新开始。