返回

力扣第 28 题:实现 strStr() 的 3 种思路,让你秒懂!

前端

剖析力扣第 28 题:掌握字符串匹配算法

引言

字符串匹配是软件开发中一项必不可少的技能。力扣第 28 题要求我们实现 strStr() 函数,以找出给定字符串 haystack 中子串 needle 的首次出现位置。

暴力匹配:简单易懂

暴力匹配是一种直接比较 haystackneedle 字符串的朴素方法。这种方法易于理解,但时间复杂度为 O(m * n),其中 m 和 n 分别是 haystackneedle 的长度。

// 暴力匹配 Java 代码
public int strStr(String haystack, String needle) {
    if (needle.isEmpty()) {
        return 0;
    }
    int n = needle.length();
    for (int i = 0; i <= haystack.length() - n; i++) {
        if (haystack.substring(i, i + n).equals(needle)) {
            return i;
        }
    }
    return -1;
}

KMP 算法:预处理提升效率

KMP 算法(Knuth-Morris-Pratt 算法)通过预处理 needle 字符串,建立一个称为 next 数组的信息表,从而优化匹配过程。next 数组存储了每个 needle 字符后缀与 needle 本身的最长公共前缀的长度。这种预处理有助于算法在遇到不匹配时快速跳过不必要的比较,从而将时间复杂度降低到 O(m + n)。

// KMP 算法 Java 代码
public int strStr(String haystack, String needle) {
    if (needle.isEmpty()) {
        return 0;
    }
    int[] next = getNext(needle);
    int i = 0;
    int j = 0;
    while (i < haystack.length() && j < needle.length()) {
        if (j == -1 || haystack.charAt(i) == needle.charAt(j)) {
            i++;
            j++;
        } else {
            j = next[j];
        }
    }
    return j == needle.length() ? i - j : -1;
}

private int[] getNext(String needle) {
    int[] next = new int[needle.length()];
    next[0] = -1;
    int i = 0;
    int j = -1;
    while (i < needle.length() - 1) {
        if (j == -1 || needle.charAt(i) == needle.charAt(j)) {
            next[++i] = ++j;
        } else {
            j = next[j];
        }
    }
    return next;
}

Boyer-Moore 算法:坏字符与好后缀

Boyer-Moore 算法通过分析 needle 字符串的特征来优化匹配过程。它维护一个 last 数组,其中存储了每个字符在 needle 字符串中的最后出现位置。该算法在遇到不匹配时利用 last 数组快速跳过不必要的比较,同时还考虑了 needle 字符串的 "好后缀" 特性,进一步提升了效率。

// Boyer-Moore 算法 Java 代码
public int strStr(String haystack, String needle) {
    if (needle.isEmpty()) {
        return 0;
    }
    int[] last = getLast(needle);
    int n = needle.length();
    int i = n - 1;
    while (i < haystack.length()) {
        int j = n - 1;
        while (j >= 0 && needle.charAt(j) == haystack.charAt(i)) {
            j--;
            i--;
        }
        if (j == -1) {
            return i + 1;
        } else {
            i += n - 1 - last[haystack.charAt(i)];
        }
    }
    return -1;
}

private int[] getLast(String needle) {
    int[] last = new int[256];
    for (int i = 0; i < 256; i++) {
        last[i] = -1;
    }
    for (int i = 0; i < needle.length(); i++) {
        last[needle.charAt(i)] = i;
    }
    return last;
}

总结:选择适合的算法

暴力匹配简单易懂,适合小规模字符串匹配场景。KMP 算法和 Boyer-Moore 算法高效快速,适用于处理大规模字符串匹配问题。在实际应用中,根据具体情况选择合适的方法尤为重要。

常见问题解答

  1. 什么是字符串匹配算法?

    • 字符串匹配算法是一种计算机程序,用于在一个大字符串中寻找一个较小字符串的出现位置。
  2. 为什么字符串匹配在软件开发中很重要?

    • 字符串匹配算法广泛应用于搜索引擎、文本编辑器和数据库查询等各种软件开发领域。
  3. KMP 算法和 Boyer-Moore 算法有什么优势?

    • 这些算法通过预处理和优化比较过程,实现了比暴力匹配更快的匹配速度。
  4. 什么时候应该使用暴力匹配算法?

    • 当字符串较小时,暴力匹配算法通常是最简单的选择。
  5. 如何选择最佳的字符串匹配算法?

    • 根据字符串长度、匹配频率和应用程序性能要求来选择算法。