76. 最小覆盖子串 完整题解 刷题笔记

76. 最小覆盖子串
leetcode 刷题笔记。

完整题解:

public class demo76 {
    public static void main(String[] args) {
        demo76 demo76 = new demo76();
        String s = demo76.minWindow("abaacbab", "abc");
        System.out.println("最小子字符串是:" + s);
    }

    Map<Character, Integer> ori = new HashMap<Character, Integer>();
    Map<Character, Integer> cnt = new HashMap<Character, Integer>();

    public String minWindow(String s, String t) {
        int tLen = t.length();
        // 将t中的全部字符解析出来放在ori中
        for (int i = 0; i < tLen; i++) {
            char c = t.charAt(i);
            ori.put(c, ori.getOrDefault(c, 0) + 1);
        }
        // 定义两个指针来控制滑动窗口的位置
        int l = 0, r = -1;
        // 这个len是用来记录最小子串长度的
        int len = Integer.MAX_VALUE, ansL = -1, ansR = -1;
        int sLen = s.length();
        while (r < sLen) {
            ++r;
            if (r < sLen && ori.containsKey(s.charAt(r))) {
                // 如果右指针这个字符属于目标字符,就把它放进cnt里面
                cnt.put(s.charAt(r), cnt.getOrDefault(s.charAt(r), 0) + 1);
            }
            // 将左指针右移,并将对应的元素移除
            while (check() && l <= r) {
                // 更新最小长度,并记录最小长度的子字符串
                if (r - l + 1 < len) {
                    len = r - l + 1;
                    ansL = l;
                    ansR = l + len;
                }
                // 如果左指针指向的这个字符串为目标字符串,则将它从cnt中移除。
                if (ori.containsKey(s.charAt(l))) {
                    cnt.put(s.charAt(l), cnt.getOrDefault(s.charAt(l), 0) - 1);
                }
                ++l;
            }
        }
        return ansL == -1 ? "" : s.substring(ansL, ansR);
    }
    // 检查滑动窗口包含的字符串是否包含所有的字符。
    public boolean check() {
        Iterator iter = ori.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            Character key = (Character) entry.getKey();
            Integer val = (Integer) entry.getValue();
            if (cnt.getOrDefault(key, 0) < val) {
                return false;
            }
        }
        return true;
    }
}


核心代码:

其他部分可以先不用看。看这部分的代码即可,它的两个while循环控制着左右指针的移动。

image-20220222211932052

外层的while控制右指针的移动。无论左指针怎么移动,右指针都会从左到右的遍历一遍。

如上图:右指针遍历到C之前,都不会进入到第二个while循环。因为不满足条件,在窗口中并没有包含所有的目标字符。直到遍历到C时(这时左指针还在开始的位置,就是A),窗口内包含了所有的目标字符(ABC),这时第二个循环的条件满足了,就进入到了第二个循环。

while (r < sLen) {
    ++r;
    if (r < sLen && ori.containsKey(s.charAt(r))) {
        // 如果右指针这个字符属于目标字符,就把它放进cnt里面
        cnt.put(s.charAt(r), cnt.getOrDefault(s.charAt(r), 0) + 1);
    }
    // 将左指针右移,并将对应的元素移除
    while (check() && l <= r) {
        // 更新最小长度
        if (r - l + 1 < len) {
            len = r - l + 1;
            ansL = l;
            ansR = l + len;
        }
        // 如果左指针指向的这个字符串为目标字符串,则将它从cnt中移除。
        if (ori.containsKey(s.charAt(l))) {
            cnt.put(s.charAt(l), cnt.getOrDefault(s.charAt(l), 0) - 1);
        }
        ++l;
    }
}

第一次进入第二个循环:

|abaac|bab,第一个竖线是左指针,第二个竖线是右指针。由于第二个循环控制左指针,左指针会一直向右移动(此时右指针不动),直到这里左指针走到这 ab|aac|bab,这个情况下滑动窗口已经不满足包含所有目标字符的条件了。就不会进入到第二个循环。此时记录最小子字符串的长度为4。

Untitled

第二次进入第二个循环:

第二次循环是这个样子的 ab | aacb | ab,此时是满足条件的,但是不会更新最小长度,因为他的长度还是4,上一次最小长度也是4。此时左指针向右移动aba | acb | ab,此时长度为3,比4小,会更新最小长度

Untitled 2
此时已经是子字符串的最小长度了,后面不会再更新最小长度的值了,但是循环会一直进行下去。

ref:滑动窗口算法解决子串问题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

dlage

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值