LeetCode题练习与总结:最小覆盖子串--76

162 篇文章 0 订阅
29 篇文章 0 订阅

一、题目描述

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

注意:

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

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

示例 2:

输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。

示例 3:

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

提示:

  • m == s.length
  • n == t.length
  • 1 <= m, n <= 10^5
  • st 由英文字母组成

二、解题思路

  1. 创建两个哈希表(或数组),分别用来记录字符串t中每个字符的出现次数以及当前窗口中每个字符的出现次数。
  2. 初始化两个指针,leftright,分别表示窗口的左右边界,初始时都指向s的第一个字符。
  3. 移动right指针扩大窗口,直到窗口中包含了t中所有的字符。
  4. 记录下当前窗口的起始位置和长度,这可能是我们要找的最小覆盖子串。
  5. 移动left指针缩小窗口,直到窗口不再包含t中所有的字符。
  6. 重复步骤3-5,直到right指针到达s的末尾。
  7. 在所有满足条件的最小覆盖子串中,返回长度最小的那个。

三、具体代码

import java.util.HashMap;

public class Solution {
    public String minWindow(String s, String t) {
        if (s == null || s.length() == 0 || t == null || t.length() == 0) {
            return "";
        }
        
        HashMap<Character, Integer> need = new HashMap<>();
        HashMap<Character, Integer> window = new HashMap<>();
        
        for (char c : t.toCharArray()) {
            need.put(c, need.getOrDefault(c, 0) + 1);
        }
        
        int left = 0, right = 0;
        int valid = 0;
        int start = 0, len = Integer.MAX_VALUE;
        
        while (right < s.length()) {
            char c = s.charAt(right);
            right++;
            
            if (need.containsKey(c)) {
                window.put(c, window.getOrDefault(c, 0) + 1);
                if (window.get(c).equals(need.get(c))) {
                    valid++;
                }
            }
            
            while (valid == need.size()) {
                if (right - left < len) {
                    start = left;
                    len = right - left;
                }
                
                char d = s.charAt(left);
                left++;
                
                if (need.containsKey(d)) {
                    if (window.get(d).equals(need.get(d))) {
                        valid--;
                    }
                    window.put(d, window.get(d) - 1);
                }
            }
        }
        
        return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
    }
}

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 遍历字符串t并构建need哈希表的时间复杂度是O(n),其中n是字符串t的长度。
  • 双指针遍历字符串s的时间复杂度是O(m),其中m是字符串s的长度。
  • 在内部循环中,每个字符最多被访问两次(一次是被right指针访问,一次是被left指针访问),因此内部循环的时间复杂度是O(m)。
  • 综上所述,总的时间复杂度是O(m + n)。
2. 空间复杂度
  • need哈希表存储字符串t中每个字符的出现次数,空间复杂度是O(n)。
  • window哈希表存储窗口中每个字符的出现次数,最坏情况下空间复杂度也是O(m)。
  • 除了哈希表之外,还有几个整型变量用于存储指针位置和计数,它们的空间复杂度是O(1)。
  • 因此,总的空间复杂度是O(m + n),主要取决于哈希表的大小。
  • 在实际情况中,如果st都是由英文字母组成,那么哈希表的大小可以认为是常数级别的,因为英文字母的数量是有限的。
  • 在这种情况下,空间复杂度可以近似认为是O(1)。

五、总结知识点

1. 哈希表(HashMap)

  • 用于存储字符串t中每个字符的出现次数(need哈希表)。
  • 用于存储窗口中每个字符的出现次数(window哈希表)。
  • 使用getOrDefault方法来避免处理不存在键的情况。
  • 使用containsKey方法来检查一个键是否存在于哈希表中。

2. 字符串操作

  • 使用toCharArray方法将字符串转换为字符数组,以便遍历字符串中的每个字符。
  • 使用charAt方法获取字符串中特定索引处的字符。

3. 双指针技术

  • 使用两个指针leftright来表示滑动窗口的左右边界。
  • 通过移动指针来扩展和收缩窗口。

4. 循环和条件语句

  • 使用while循环来遍历字符串s,直到right指针到达字符串的末尾。
  • 使用内部while循环来在满足条件时收缩窗口。

5. 变量和赋值

  • 使用变量valid来跟踪窗口中满足need条件的字符数量。
  • 使用变量startlen来记录最小覆盖子串的起始位置和长度。

6. 字符串截取

  • 使用substring方法来截取最终的最小覆盖子串。

7. 边界检查

  • 在方法开始时检查输入字符串是否为空或长度为零,以避免空指针异常和无效操作。

8. 整数常量

  • 使用Integer.MAX_VALUE作为初始的最小覆盖子串长度,以便在找不到覆盖子串时能够识别。

9. 逻辑比较

  • 使用equals方法来比较两个整数对象的内容是否相等,而不是比较它们的引用。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一直学习永不止步

谢谢您的鼓励,我会再接再厉的!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值