小c的字符串匹配问题

113 阅读3分钟

问题描述

小C拥有两个长度为 n 的字符串 s 和 t。字符串 s 中最多有 10 个不同的字符。她希望找到一些区间 [l, r],在这些区间中,对于任意的 i ∈ [l, r],总满足 s_i = t_i,称这样的区间为匹配区间

小C还可以对字符串 s 进行一些修改:她可以选择字符串 s 中的一个字符 s_i,将其放入集合 S,并将 s_i 修改为任意字符。集合 S 中的不同字符数量不能超过 k

她想知道,在最多修改 k 个不同字符的情况下,能够获得的最大匹配区间数量是多少。


测试样例

样例1:

输入:n = 3 , k = 1 , s = "abc" , t = "azc"
输出:5

样例2:

输入:n = 5 , k = 2 , s = "apple" , t = "ample"
输出:44

样例3:

输入:n = 6 , k = 2 , s = "banana" , t = "bandna"
输出:45

问题理解

  1. 匹配区间:我们需要找到一些区间 [l, r],在这些区间中,对于任意的 i ∈ [l, r],总满足 s_i = t_i
  2. 修改操作:我们可以对字符串 s 进行一些修改,选择字符串 s 中的一个字符 s_i,将其放入集合 S,并将 s_i 修改为任意字符。集合 S 中的不同字符数量不能超过 k
  3. 目标:在最多修改 k 个不同字符的情况下,找到能够获得的最大匹配区间数量。

数据结构选择

  • 滑动窗口:我们可以使用滑动窗口来动态地维护当前窗口内的字符匹配情况。
  • 哈希表:使用哈希表来记录当前窗口内需要修改的字符及其数量。

算法步骤

  1. 初始化

    • 使用两个指针 left 和 right 来表示当前窗口的左右边界。
    • 使用一个哈希表 charCount 来记录当前窗口内需要修改的字符及其数量。
  2. 扩展窗口

    • 移动 right 指针,扩展窗口。
    • 如果 s[right] != t[right],则将 s[right] 加入 charCount
  3. 缩小窗口

    • 如果 charCount 中的不同字符数量超过 k,则移动 left 指针,缩小窗口,直到 charCount 中的不同字符数量不超过 k
  4. 计算匹配区间数量

    • 在每次扩展或缩小窗口后,计算当前窗口内的匹配区间数量,并更新最大匹配区间数量。

总结

通过滑动窗口和哈希表的结合,我们可以动态地维护当前窗口内的字符匹配情况,并在不超过 k 次修改的情况下,找到最大的匹配区间数量。

优化思路

  1. 减少哈希表操作

    • 当前代码在每次扩展和缩小窗口时都会频繁地操作哈希表,这可能会导致性能瓶颈。我们可以考虑减少哈希表的操作次数。
  2. 提前计算匹配字符

    • 在扩展窗口时,如果 s[right] == t[right],我们可以直接跳过这个字符,因为不需要修改它。
  3. 优化窗口缩小条件

    • 当前代码在每次扩展窗口后都会检查 charCount 的大小是否超过 k,这可能会导致不必要的缩小操作。我们可以考虑在扩展窗口时提前判断是否需要缩小窗口。
public static int solution(int n, int k, String s, String t) {
        int left = 0, right = 0;
        int maxMatchCount = 0;
        Map<Character, Integer> charCount = new HashMap<>();

        while (right < n) {
            // 扩展窗口,更新 charCount
            char rightChar = s.charAt(right);
            if (s.charAt(right) != t.charAt(right)) {
                charCount.put(rightChar, charCount.getOrDefault(rightChar, 0) + 1);
            }
            right++;

            // 如果 charCount 中的不同字符数量超过 k,则缩小窗口
            while (charCount.size() > k) {
                char leftChar = s.charAt(left);
                if (s.charAt(left) != t.charAt(left)) {
                    charCount.put(leftChar, charCount.get(leftChar) - 1);
                    if (charCount.get(leftChar) == 0) {
                        charCount.remove(leftChar);
                    }
                }
                left++;
            }

            // 计算当前窗口内的匹配区间数量
            int currentMatchCount = right - left;
            maxMatchCount = Math.max(maxMatchCount, currentMatchCount);
        }

        return maxMatchCount;
    }