力扣第七十六题-最小覆盖子串

558 阅读3分钟

「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战

前言

力扣第七十六题 最小覆盖子串 如下所示:

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

输入: s = "ADOBECODEBANC", t = "ABC"
输出: "BANC"

示例 2:

输入: s = "a", t = "a"
输出: "a"

一、思路

题目稍微有点长,我们可以从中得到以下的关键信息:

  • 子字符串中字符数量应大于等于 t 中的数量
  • 需要找到该 最小覆盖子串 并输出

我们需要注意 最小覆盖子串 中是有可能会有重复字符的,例如 s = "ECABAD", t = "ABCD" 的最小覆盖子串是 CABAD

假设 i ~ j 中间包含了字符串 t 所需的字符,那么如何找到最小覆盖子串呢?

大致的实现步骤,如下所示:

  1. 遍历字符串 t 并将各字符计数,便于后续判断
  2. 遍历字符串 s,判断当前字符是否包含 t
    • 如果不包含:继续向下遍历
    • 如果包含:丢弃左边第一个必要的字符
  3. 返回结果即可

举个例子

此处以示例中的 s = "ADOBECODEBANC", t = "ABC" 作为例子

  1. 遍历 t 并计数,得到所需的字符个数
  2. 开始遍历字符串 s
  3. i = 5 时,子串 ADOBEC 含有 ABC。更新结果为 ADOBEC
  4. 丢弃子串 ADOBEC 左边首个必要元素,丢弃后为 DOBEC
  5. 再继续向右便利,当 i=10 时,此时子串的长度不小于当前结果,故丢弃。
  6. 重复上述的过程,值得注意的是:在找到子串后,如果丢弃了非必要字符,需要判断是否需要更新结果
  7. 最终可以找到最小覆盖子串 BANC

二、实现

实现代码

实现代码与思路中保持一致

    public String minWindow(String s, String t) {
        HashMap<Character,Integer> need = new HashMap<>();  // 需要的字符及数量
        HashMap<Character,Integer> had = new HashMap<>();   // 当前字串中出现的字符及数量
        // 初始化需要的字符数量
        for(int i = 0;i < t.length();i ++){
            need.put(t.charAt(i),need.getOrDefault(t.charAt(i), 0) + 1);
        }
        String ans = "";
        int len = Integer.MAX_VALUE;
        int cnt = 0;  // 当前子串有多少有效字符
        // 窗口向右滑动
        int left = 0, right = 0;
        while (right < s.length()) {
            // 向右扩展
            char c = s.charAt(right);
            had.put(c, had.getOrDefault(c, 0) + 1); // 已有的字符串中计数
            if(need.containsKey(c) && had.get(c) <= need.get(c)) {
                cnt ++;
            }
            if (cnt == t.length()) {    // 如果找到了结果
                if (right -left + 1 < len) {
                    len = right - left + 1;
                    ans = s.substring(left, right+1);
                }
                // 向右压缩
                while (left < right){
                    char leftC = s.charAt(left);
                    had.put(leftC, had.get(leftC) -1);
                    left++;
                    // 找到第一个必要元素丢弃
                    if (need.containsKey(leftC) && had.get(leftC) < need.get(leftC)) {
                        cnt--;
                        break;
                    } else {    // 比较结果
                        if (right -left + 1 < len) {
                            len = right - left + 1;
                            ans = s.substring(left, right+1);
                        }
                    }
                }
            }
            right++;
        }
        return ans;
    }

测试代码

    public static void main(String[] args) {
        new Number76().minWindow("ADOBECODEBANC", "ABC");
    }

结果

image.png

三、总结

感谢看到最后,非常荣幸能够帮助到你~♥

如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~