力扣LeetCode #76最小覆盖子串(MinWindow)

这篇博客介绍了如何利用滑动窗口算法解决LeetCode中的第76题,即找到一个最小子串,涵盖目标字符串t的所有字符。博主通过示例解释了题目要求,并详细阐述了滑动窗口法的思路,包括处理目标字符串,记录窗口边界,以及检查窗口是否满足条件的过程。最后,提供了JAVA实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

- 题目描述

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。

提示:
1 <= s.length, t.length <= 105
s 和 t 由英文字母组成

进阶:你能设计一个在 o(n) 时间内解决此问题的算法吗?

来源:LeetCode

- 示例

  • 示例 1:
    输入:s = “ADOBECODEBANC”, t = “ABC”
    输出:“BANC”
  • 示例 2:
    输入:s = “a”, t = “a”
    输出:“a”

- 思路分析

  • 直接暴力求解不太现实,如果字符串很长的话,效率会非常低。因为要找的是一个符合条件的子串,可以想到会用滑动窗口法:即找到一个长度最小的窗口,包含目标字符串中的所有字符。滑动窗口法有两种情况:①窗口大小固定;②窗口大小不固定。此处即第二种。
  • 在寻找窗口之前,我们先处理一下目标字符串 t t t。因为窗口需要满足目标字符串的长度和对应字符的个数,我们将 t t t < C h a r a c t e r − I n t e g e r > <Character-Integer> <CharacterInteger>的形式保存在字典里,记录每个字符以及对应个数。保存 l e n t len_t lent,表示字符串 t t t的长度,用来监控窗口中的符合要求的字符总长度是否已经达到了字符串 t t t的长度。
  • 在找窗口的过程中,我们记录两个值: s t a r t start start e n d end end s t a r t start start记录窗口的左边界, e n d end end记录窗口的右边界,窗口的内容即为 [ s t a r t , e n d ) [start, end) [start,end)。从 0 0 0开始,我们将 e n d end end向右滑动,遇到一个字符即判断它是否是目标字符串中的字符:如果是,就将字典中该字符的个数 − 1 -1 1,表示找到了一个。如果在 − 1 -1 1之前,该字符剩余要找的个数 > = 1 >=1 >=1,将 l e n t − 1 len_t-1 lent1。需要保存 l e n t len_t lent的原因就在这里:比如 s = A A B s=AAB s=AAB t = A B t=AB t=AB,如果不保存 l e n t len_t lent,而只看有多少个在 t t t中的字符,那么 s s s中的 A A AA AA也会被看做是一个符合要求的窗口。但实际上,检查到第一个 A A A时,字典中的 < A : 1 > <A:1> <A:1>会变成 < A : 0 > <A:0> <A:0> l e n t − 1 len_t-1 lent1;检查到第二个 A A A时,字典中的 < A : 0 > <A:0> <A:0>会变成 < A : − 1 > <A:-1> <A:1>,但由于本来是 < A : 0 > <A:0> <A:0>,说明已经不需要 A A A了,因此 l e n t len_t lent不会 − 1 -1 1,会继续检查下一个字符。
  • l e n t len_t lent变为 0 0 0, 说明已经找到了一个符合条件的窗口。而我们需要的窗口尽可能小,因此我们从 0 0 0开始向右移动左边界 s t a r t start start,并检查遇到的每一个字符:如果不要这个字符,剩下的字符串是否仍然使 l e n t = 0 len_t=0 lent=0。具体方法是:如果这个字符不在t中,则可以去掉;如果在 t t t中,则需要将其字典中的值 + 1 +1 +1,如果该值现在 > 0 >0 >0,说明这个字符是必不可少的, l e n t + 1 len_t+1 lent+1。如果 l e n t len_t lent变为 > 0 >0 >0,说明现在的窗口不满足条件,因此 e n d end end需要再次开始向右滑动,重复上一步的过程。

- JAVA实现

class Solution {
    public String minWindow(String s, String t) {
        HashMap map = new HashMap();
        int len_s = s.length();
        int len_t = t.length();
        if(len_s<len_t) return "";
        for(int i=0; i<len_t; i++) {
            char ch = t.charAt(i);
            if(!map.containsKey(ch)) map.put(ch, 1);
            else map.put(ch, (int)map.get(ch)+1);
        }
        
        int end = 0, start = 0;
        int minLeft = 0, minRight = Integer.MAX_VALUE;    //这样设置保证如果有一个解,则一定能够使minLeft和minRight被改变
        while(end < len_s) {
            if(map.containsKey(s.charAt(end))) {
                if((int)map.get(s.charAt(end)) >= 1) len_t--;
                map.put(s.charAt(end), (int)map.get(s.charAt(end)) - 1);
            }
            end++;
            while(len_t == 0) {  //缩小左边界"到第一个出现t中字母的位置
                if(map.containsKey(s.charAt(start))) {
                    map.put(s.charAt(start), (int)map.get(s.charAt(start))+1);
                    if((int)map.get(s.charAt(start)) > 0) len_t++;
                }
                if(end - start < minRight - minLeft) {
                    minRight = end;
                    minLeft = start;
                }
                start++; 
            }
        }
        return minRight==Integer.MAX_VALUE?"":s.substring(minLeft, minRight);        
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值