104.最小覆盖子串

一、题目描述

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

注意:

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

二、解题思路

使用滑动窗口,s="ABAACBAB"T="ABC",定义两个指针来遍历,使用变量minLen来记录窗口长度:
在这里插入图片描述
第一步:right不停的向后移动,直到窗口中的元素涵盖了所有的T中的元素,此时minLen=5
在这里插入图片描述
第二步:尝试缩小窗口,先将left向后移动一位,移动之后判断此时窗口中是否还包含T中的所有元素,如果包含则更新minLen,此时minLen=4:
在这里插入图片描述

left继续右移,发现此时的窗口不能再继续包含所有T中的元素,所以需要扩大窗口了,right向后移动,之后继续重复第一、二步即可,每次minLen更新是当前窗口中包含所有T中的元素,且当前窗口长度小于minLen

最终结果如下,right遍历完整个字符串,此时minLen=3,返回输出子串为ACB或者CBA都可以.
在这里插入图片描述

三、代码演示

class Solution {
    public String minWindow(String s, String t) {
        //统计字符串t中每个字符出现的次数:26个大写,26个小写字母,它们的ASCII码之间还有8个其它字符
        int[] cntT = new int[60];
        //统计t中有多少个不同的字符
        int uniqueCharInt = 0;
        for (char c : t.toCharArray()){
            if (cntT[c-'A']==0){
                uniqueCharInt++;
            }
            cntT[c-'A']++;
            
        }
        int left=0, right=0;
        //窗口中每个字符出现的次数
        int[] windowCntS = new int[60];
        //记录当前窗口中字符出现的次数和t中字符出现次数相等的字符数
        int matchedChars =0;
        //结果集:-1表示字串最小长度,两个0分别表示left和right边界
        int[] ans = {-1,0,0};
        while (right<s.length()){
            char rightChar = s.charAt(right);
            //计算right指针指向元素的索引
            int rightCharIndex = rightChar-'A';
            //当前窗口中rightCharIndex所指元素出现过,所以次数加一次
            windowCntS[rightCharIndex]++;

            //如果窗口里某个字符出现的次数等于了这个字符在t中出现的次数,说明这个字符匹配上了
            if (windowCntS[rightCharIndex]==cntT[rightCharIndex]){
                matchedChars++;
            }

            //如果窗口中出现了所有t中的元素,说明找到了符合条件的字串
            while (left<=right && matchedChars==uniqueCharInt){
                //尝试缩减窗口,因为找的是最短符合条件的字串
                if (ans[0]==-1 || right-left+1 < ans[0]){
                    ans[0] = right-left+1;
                    ans[1] = left;
                    ans[2] = right;
                }

                //要缩短窗口,left右移,窗口中当前left所指元素要移除掉
                char leftChar = s.charAt(left);
                int leftCharIndex = leftChar-'A';
                windowCntS[leftCharIndex]--;

                //如果窗口中当前left所指字符出现的次数小于它在t里面出现的次数,说明该元素不匹配
                if (windowCntS[leftCharIndex] < cntT[leftCharIndex]){
                    //已匹配的字符总数减一
                    matchedChars--;
                }
                left++;
            }
            right++;
            
        }
        return ans[0] == -1 ? "" : s.substring(ans[1],ans[2]+1);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值