76. 最小覆盖子串

题目:

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

注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

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

输入:s = “a”, t = “a”
输出:“a”

提示:

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

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

思路

这是我第一次硬刚困难难度的题目,其恶心程度远超我想象。要在O(n)的时间内解决问题,那肯定不能用暴力解法,只能一次遍历字符串,然后计算得到结果。
因此,肯定是要用滑动窗口来解决问题。
1、我们在字符串S中使用双指针中的左右指针技巧,初始化left = right = 0,把索引左闭右开区间[left, right)称为一个「窗口」。

2、我们先不断地增加right指针扩大窗口[left, right),直到窗口中的字符串符合要求(包含了T中的所有字符)。

3、此时,我们停止增加right,转而不断增加left指针缩小窗口[left, right),直到窗口中的字符串不再符合要求(不包含T中的所有字符了)。同时,每次增加left,我们都要更新一轮结果。

4、重复第 2 和第 3 步,直到right到达字符串S的尽头。

代码

public String minWindow(String s, String t) {
    //创建两个HashMap用来计数,need记录目标字符串中每个字符出现的次数,window记录窗口覆盖的字符串
        HashMap<Character,Integer> window= new HashMap<>();
        HashMap<Character,Integer> need= new HashMap<>();

    	//初始化need和window,其中need需要注意的是,有些字符可能出现多次
        for(int i=0;i<t.length();i++){
            Integer val=need.getOrDefault(t.charAt(i),0);
            val++;
            need.put(t.charAt(i),val);
        }
        for(int j=0;j<s.length();j++){
            window.put(s.charAt(j),0);
        }
		
    	//初始化窗口长度,窗口左闭右开
        int left=0,right=0,valid=0,len=9999999,start=0;

    	//先让窗口变大,直到能够覆盖目标字符串
        while(right<s.length()){
            //右指针移,扩大窗口
            Character c=s.charAt(right);
            right++;
            if(need.containsKey(c)){
                Integer val1=window.get(c);
                val1++;
                window.put(c,val1);
                if(window.get(c).equals(need.get(c))){
                 //   if(window.get(c)==need.get(c)){
                    valid++;
                }
            }

            //当valid等于need的大小之后,说明此时窗口已经能够包括所有目标字符串了,此时要缩小窗口
            while(valid==need.size()){
                if(right-left<len){
                    start=left;
                    len=right-left;
                }
                //左指针移动,缩小窗口
                Character d=s.charAt(left);
                left++;
                if(need.containsKey(d)){
                    if(window.get(d).equals(need.get(d)))
                    valid--;
                Integer val2=window.get(d);
                val2--;
                window.put(d,val2);
                }
            }

        }
        
        if(len==9999999||t.length()>s.length()){
            return "";
        }else if(s.equals(t)){
            return s;
        }else {
            return s.substring(start,start+len);
        }
}

在这里插入图片描述

注意

然后需要注意的是,在力扣的测试案例中,最后有一个特别特别大的测试案例,那个案例卡了我特别久,原因是因为定义的Map里的Integer是对象,Integer会缓存频繁使用的数值[-128,127],超过此范围就会new一个对象,导致使用“==”错误,改为equals()即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值