力扣——最小覆盖子串

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

示例

输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”
说明

如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。

思路
  

  1. 如果使用暴力遍历子串的方法,肯定不是该题的最优解
  2. 使用动态窗口的方法,leftright两个指针来限定窗口的范围
  3. 窗口进行变化的规则:right往右走表示窗口的扩展,left往右走表示窗口的收缩。先将窗口进行扩展,如果窗口覆盖了所有子串,这时说明right位置的元素肯定是需要的,但是left由于是最开始的,有可能时不需要的,这时将窗口进行收缩,如果收缩的下标是需要的,则上一时刻窗口已经是一个最小时刻了,收缩后可能不满足条件,然后进行下一次匹配,窗口继续进行扩张。如果收缩的下标是不需要的,那么窗口就会不断缩小,直到缩小到一个最小时刻为止。这样重复该过程,遍历完整个主串,就会在该过程中筛选出所有满足的情况,再筛选出最小的
  4. 那么如何去判断是否覆盖子串,可以用一个map来记录子串中的元素的需要情况,因为子串中的元素可能出现重复,key为要记录的元素,value是完成覆盖需要的个数
        value = 0 ,表示刚好满足,不需要
        value > 0 ,表示需要value个
        value < 0 ,表示多余value的绝对值个
  5. 在第四条中已经维护了一个map,去随时更新窗口中的覆盖情况,如何判断是否满足,肯定不可能每次去遍历,这样时间复杂度会很高,所以可以维护一个计数器,用来计数子串中已经覆盖的个数,比如子串“abc”,那么刚开始计数器count=3,当窗口中有a了后,count=2,这样当count=0时,也就能判断完成了覆盖。
  6. 维护count值的变化并不是说每次都需要更新。当扩张时,有可能窗口中已经满足了覆盖了这个元素,已经不需要了,但是map需要进行维护,count是不用变化的,如果子串需要这个元素,count就要减一。同样,当缩小时,要判断缩小的这个元素我是否多余,如果没有多余,那么count就要加一
public static String minWindow(String s, String t) {
        if(s == null || t == null || s.length() <= 0 || t.length() <= 0  || s.length()<t.length()) {
            return "";
        }
        //定义左右两个指针,right=-1的意义在于上去就要先走一步
        int left = 0;
        int right = -1;
        //定义窗口的最小值,窗口的最小字符串
        int min = s.length();
        String minS = "";

        //定义一个map,统计t中的各字符个数
        HashMap<Character, Integer> map = new HashMap<>();
        for (int i = 0; i < t.length(); i++) {
            Integer num = map.get(t.charAt(i));
            if(num == null) {
                map.put(t.charAt(i),1);
            } else {
                map.put(t.charAt(i),num+1);
            }
        }
        //记录t中还需要有的字母个数
        int count = t.length();

        //开始窗口移动
        while (true) {
            //窗口只能向一个方向移动,判断扩张还是收缩,如果满足情况则收缩,否则扩张
            if(count <= 0) {
                if(min >= (right-left+1)) {
                    min = (right-left+1);
                    minS = s.substring(left,right+1);
                }
                //收缩过程
                left++;
                if(left >= s.length()) {
                    break;
                }
                char leftCur = s.charAt(left-1);
                Integer num = map.get(leftCur);
                if(num == null) {
                    continue;
                }
                if(num >= 0) {
                    count++;
                }
                map.put(leftCur,num+1);
            } else {
                //扩张过程,向右一步
                right++;
                if(right >= s.length()) {
                    break;
                }
                char rightCur = s.charAt(right);
                Integer num = map.get(rightCur);
                if(num == null) {
                    continue;
                }
                if(num > 0) {
                    count--;
                }
                map.put(rightCur,num-1);
            }
        }
        return minS;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值