LeetCode 28. 实现 strStr() 字符串匹配(BP&KMP)

Posted ZSYL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 28. 实现 strStr() 字符串匹配(BP&KMP)相关的知识,希望对你有一定的参考价值。

LeetCode 28. 实现 strStr 字符串匹配(BP&KMP)

题目描述

实现 strStr() 函数。

给你两个字符串 haystackneedle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。

说明:

当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。

示例 1:

输入:haystack = "hello", needle = "ll"
输出:2

示例 2:

输入:haystack = "aaaaa", needle = "bba"
输出:-1

本题是经典的字符串单模匹配的模型,因此可以使用字符串匹配算法解决,常见的字符串匹配算法包括暴力匹配、Knuth-Morris-Pratt 算法、Boyer-Moore 算法、Sunday 算法等,本文将讲解 Knuth-Morris-Pratt 算法。

方法一:暴力匹配

思路及算法

我们可以让字符串 needle 与字符串 haystack 的所有长度为 m 的子串均匹配一次。

为了减少不必要的匹配,我们每次匹配失败即立刻停止当前子串的匹配,对下一个子串继续匹配。如果当前子串匹配成功,我们返回当前子串的开始位置即可。如果所有子串都匹配失败,则返回 −1。

class Solution {
    public int strStr(String haystack, String needle) {
        int n = haystack.length(), m = needle.length();
        for (int i = 0; i + m <= n; i++) {
            boolean flag = true;
            for (int j = 0; j < m; j++) {
                if (haystack.charAt(i + j) != needle.charAt(j)) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                return i;
            }
        }
        return -1;
    }
}

复杂度分析

  • 时间复杂度:O(n×m),其中 n 是字符串 haystack 的长度,m 是字符串 needle 的长度。最坏情况下我们需要将字符串 needle 与字符串 haystack 的所有长度为 m 的子串均匹配一次。
  • 空间复杂度:O(1)。我们只需要常数的空间保存若干变量。

方法二:Knuth-Morris-Pratt 算法

思路及算法


我们举个例子说明:字符串 aabaaab 的前缀函数值依次为 0,1,0,1,2,2,3。


有了前缀函数,我们就可以快速地计算出模式串在主串中的每一次出现。

这样我们可以将代码实现分为两部分:

  1. 第一部分是求 needle 部分的前缀函数,我们需要保留这部分的前缀函数值。
  2. 第二部分是求 haystack 部分的前缀函数,我们无需保留这部分的前缀函数值,只需要用一个变量记录上一个位置的前缀函数值即可。当某个位置的前缀函数值等于 m 时,说明我们就找到了一次字符串 needle 在字符串 haystack 中的出现(因为此时真前缀恰为字符串 needle,真后缀为以当前位置为结束位置的字符串 haystack 的子串),我们计算出起始位置,将其返回即可。
class Solution {
    public int strStr(String haystack, String needle) {
        int n = haystack.length(), m = needle.length();
        if (m == 0)
            return 0;
        int[] next =  new int [m];
        next[0] = 0;  // 也有初始-1的next数组
        for (int i = 1, j = 0; i < m; i++) {  // 每一轮j都从0开始
            while (j > 0 && needle.charAt(i) != needle.charAt(j)) {
                j = next[j - 1];  // 回退到上一个位置的公共前后缀
            }
            if (needle.charAt(i) == needle.charAt(j)) {
                j++;  // 长度++
            }
            next[i] = j;  // 当前位置最长公共前后缀
        }
        for (int i = 0, j = 0; i < n; i++) {
            while (j > 0 && haystack.charAt(i) != needle.charAt(j)) {
                j = next[j - 1];
            }
            if (haystack.charAt(i) == needle.charAt(j)) {
                j++;  // 长度++
            }
            if (j == m) {
                return i - m + 1;
            }
        }
        return -1;
    }
}

复杂度分析

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

加油!

感谢!

努力!

以上是关于LeetCode 28. 实现 strStr() 字符串匹配(BP&KMP)的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode 28 - 实现 strStr():问题

LeetCode 28 实现 strStr()

leetcode 28. Implement strStr() 实现strStr()

leetcode-----28. 实现 strStr()

LeetCode#28 | Implement strStr() 实现strStr()

python刷LeetCode:28. 实现 strStr()