详谈滑动窗口算法与KMP算法区别以及二者在什么场景下使用

什么是滑动窗口算法

滑动窗口算法是一种用于解决数组(或字符串)中子数组(或子字符串)问题的算法。该算法通过维护一个固定大小的窗口(通常是两个指针),该窗口在数组上滑动,以寻找符合特定条件的子数组。算法的基本思想是通过调整窗口的起始和结束位置来遍历整个数组,以找到满足特定条件的子数组。这个窗口通常是连续的,但具体的实现方式可以根据问题的要求而变化。

滑动窗口算法的一般步骤

滑动窗口算法的一般步骤如下:

  1. 初始化: 定义两个指针,通常表示窗口的起始位置(left)和结束位置(right),并初始化它们。

  2. 滑动窗口: 移动右指针,直到满足某个条件为止。这个条件可以根据具体问题而定,比如找到一个符合要求的子数组或达到某种状态。

  3. 调整窗口大小: 当右指针移动到某个位置后,如果满足了条件,尝试缩小窗口大小,即移动左指针,直到不再满足条件为止。

  4. 重复操作: 不断重复步骤2和步骤3,直到遍历完整个数组。

滑动窗口算法通常用于解决一些优化问题,例如在一个数组中找到最短的子数组,使得其满足特定的条件,或者在一个字符串中找到最短的子串,包含目标子串中的所有字符。这种算法的时间复杂度通常较低,因为它避免了不必要的重复计算。

滑动窗口算法是如何实现的 

下面我会以一个具体问题为例,演示如何使用 Java 代码实现滑动窗口算法。我们以「找到字符串中无重复字符的最长子串」为例,实现滑动窗口算法。

import java.util.HashMap;

public class LongestSubstringWithoutRepeating {

    public static int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }

        // 用于存储字符和其在字符串中的位置
        HashMap map = new HashMap<>();
        int maxLength = 0;
        int left = 0;

        for (int right = 0; right < s.length(); right++) {
            char currentChar = s.charAt(right);

            // 如果字符已经在窗口中,更新左指针的位置
            if (map.containsKey(currentChar)) {
                left = Math.max(map.get(currentChar) + 1, left);
            }

            // 更新字符的位置
            map.put(currentChar, right);

            // 更新最大长度
            maxLength = Math.max(maxLength, right - left + 1);
        }

        return maxLength;
    }

    public static void main(String[] args) {
        String input = "abcabcbb";
        int result = lengthOfLongestSubstring(input);
        System.out.println(result); // 输出 3(对应的无重复字符子串为 "abc")
    }
}

上述代码实现了寻找字符串中无重复字符的最长子串的长度。核心是通过维护一个窗口,用 leftright 指针表示窗口的左右边界,通过遍历字符串,不断调整窗口的大小,同时利用哈希表记录每个字符最后一次出现的位置,以实现快速的查找和更新。

什么是KMP算法

KMP(Knuth-Morris-Pratt)算法是一种用于在文本串中查找模式串出现位置的字符串匹配算法。它的主要特点是在匹配失败时,能够利用已经得到的部分匹配结果,避免不必要的比较,从而提高匹配效率。

KMP算法的核心思想是构建一个部分匹配表(Partial Match Table),也称为「next数组」,用于指导匹配过程中的跳跃。这个表记录了模式串中每个前缀的最长相等前缀后缀的长度。通过在匹配过程中利用这个表,算法能够在匹配失败时迅速调整模式串的位置,继续匹配,而不用回溯到文本串中的前面位置。

KMP算法的基本步骤

  1. 构建部分匹配表: 遍历模式串,对于每个位置,找到最长的相等的前缀后缀的长度,并将这个长度记录在部分匹配表中。

  2. 匹配过程: 在匹配文本串和模式串的过程中,当匹配失败时,根据部分匹配表中的值调整模式串的位置,继续匹配。

KMP算法的时间复杂度是 O(m + n),其中 m 是模式串的长度,n 是文本串的长度。相比暴力匹配算法的 O(mn) 复杂度,KMP算法在处理大规模文本串和模式串时具有更高的效率。

public class KMPAlgorithm {

    public static int[] buildNext(String pattern) {
        int[] next = new int[pattern.length()];
        int j = 0;

        for (int i = 1; i < pattern.length(); i++) {
            while (j > 0 && pattern.charAt(i) != pattern.charAt(j)) {
                j = next[j - 1];
            }

            if (pattern.charAt(i) == pattern.charAt(j)) {
                j++;
            }

            next[i] = j;
        }

        return next;
    }

    public static int kmpSearch(String text, String pattern) {
        int[] next = buildNext(pattern);
        int j = 0;

        for (int i = 0; i < text.length(); i++) {
            while (j > 0 && text.charAt(i) != pattern.charAt(j)) {
                j = next[j - 1];
            }

            if (text.charAt(i) == pattern.charAt(j)) {
                j++;
            }

            if (j == pattern.length()) {
                return i - j + 1; // 匹配成功,返回起始位置
            }
        }

        return -1; // 未找到匹配
    }

    public static void main(String[] args) {
        String text = "ABABCABAB";
        String pattern = "ABAB";
        int result = kmpSearch(text, pattern);
        System.out.println(result); // 输出 2
    }
}

这个示例中,buildNext 方法用于构建部分匹配表,而 kmpSearch 方法则实现了KMP算法的匹配过程。

二者有何区别

应用场景和实现原理有一些不同

  1. 滑动窗口算法:

    • 应用场景: 主要用于解决子串问题,即在一个字符串中找到一个连续的子串,满足特定的条件。
    • 实现原理: 通过维护一个可变大小的窗口,该窗口在字符串上滑动,同时根据问题的要求调整窗口的大小。该算法通常通过两个指针,一个指向窗口的起始位置,另一个指向窗口的结束位置,来遍历整个字符串。
    • 示例应用: 最小覆盖子串、最长无重复字符子串等问题。
  2. KMP算法(Knuth-Morris-Pratt算法):

    • 应用场景: 主要用于在一个文本串中查找一个模式串的出现位置。
    • 实现原理: KMP算法利用了模式串中已匹配的信息,避免不必要的回溯。它通过构建一个部分匹配表(Partial Match Table),来指导匹配过程中的跳跃。这个表记录了模式串每个前缀的最长相等前缀后缀的长度。
    • 示例应用: 在文本编辑器中查找文本中的关键字等。

区别:

  • 问题类型: 滑动窗口算法主要用于子串问题,而KMP算法主要用于模式匹配问题。
  • 实现原理: 滑动窗口算法通过维护一个窗口在字符串上的滑动来解决问题,而KMP算法利用构建的部分匹配表来优化模式匹配过程。
  • 应用场景: 滑动窗口算法更适用于连续的子串匹配问题,而KMP算法更适用于在文本中查找模式串的位置。

总体而言,虽然滑动窗口算法和KMP算法有不同的应用场景,但它们都是在字符串处理领域中解决特定问题的有效工具。选择使用哪种算法取决于具体的问题需求。

你可能感兴趣的:(算法)