含有所有字符的最短字符串 | 循序递进---@二十一画

题目:

含有所有字符的最短字符串

给定两个字符串 s 和 t 。返回 s 中包含 t 的所有字符的最短子字符串。如果 s 中不存在符合条件的子字符串,则返回空字符串 “” 。

如果 s 中存在多个符合条件的子字符串,返回任意一个。

注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最短子字符串 "BANC" 包含了字符串 t 的所有字符 'A''B''C'
输入:s = "a", t = "a"
输出:"a"
输入:s = "a", t = "aa"
输出:""
解释:t 中两个字符 'a' 均应包含在 s 的子串中,因此没有符合条件的子字符串,返回空字符串。

提示:

  • 1 <= s.length, t.length <= 10^5
  • st 由英文字母组成
  • **进阶:**你能设计一个在 o(n) 时间内解决此问题的算法吗?

分析:

相似题目:

拆解关键词:

【含所有字符、最短字串、对于重复字符寻找到的字串需要大于等于该字符的出现次数】

优先考虑:

【滑动窗口】

想法:

我这里优先考虑滑动窗口进行求解,因为这种类型的题看题目,要求是连续字串、且满足题目要求、还要最短最优结果,那么使用滑动窗口大方向应该是对的,如果实现不了,我们再修改思路。

举例:假如有如下字符串:

s:A B D E C O A B C

t:A B C

要求在S中寻找包含T所有元素【对于任意单个元素来说,S中出现的该元素次数也要大于等于T中出现的次数】

定义窗口window,left,right 初始化为 0

①从位置index=0开始遍历,找到第一个解:A B D E C

此时以index=0位开始的最优解就是上面所述,left指针不动的情况,再移动right指针只会让解变得越来越冗余。现在需要移动left指针去寻找满足条件的潜在的优解

②移动left指针,当移动left指针之后还满足条件的话, 那么记录下当前的解,如果不满足题目条件,那么再继续移动right指针。

⚠️简单来说,思路就是,当我们寻找到一个解的时候,我们不确定这个解具体的位置:举例:

s:C E A B C O A B C

t:A B C

我们找到的第一个解应该是 C E A B C,但是可以发现其实这个解具体的位置是在下标= 2 3 4 ,而和前面的下标=1 2 没有关系,所以移动left指针 保持right不动只是想要寻找这个解的具体位置下标。

当我们找到之后,那么再继续移动right指针,加大范围,去寻找其他可能存在的解。思路都是一样的。

代码:
滑动窗口:

在这里插入图片描述

class Solution {
    public String minWindow(String s, String t) {

        //0、排除特殊情况
        if(s.length()<t.length()) return "";
        if(s.trim().length()<1) return "";
        if(t.trim().length()<1) return "";

        //1、初始化
        int s_len = s.length();
        int t_len = t.length();
        int left = 0;
        int right = 0;

        HashMap<Character,Integer> Smap = new HashMap<>();
        HashMap<Character,Integer> Tmap = new HashMap<>();

        String res = s+"a";//后面用来判断是否无解
        
        //初始化Tmap,代表t中出现的元素及其出现次数
        for(int i =0;i<t_len;i++){
            char tmp = t.charAt(i);
            Tmap.put(tmp,Tmap.getOrDefault(tmp,0)+1);
        }

        //2、开始循环
        while(right<s_len){

            if(s_len-left<t.length()) return res; //剩余元素个数已经不足 直接返回结果

            while(right<s_len && !isEqual(Smap,Tmap)){
                Smap.put(s.charAt(right),Smap.getOrDefault(s.charAt(right),0)+1);
                right++;
            }

            //执行到这里,要么是越界,要么是找到了解
            //修改left位置,继续循环  当此时数组不相同的时候,就是left的新位置
            while(left<right && isEqual(Smap,Tmap)){

                res = res.length()>(right-left)?s.substring(left,right):res; //依据新解的长度来更新旧解

                Smap.put(s.charAt(left),Smap.getOrDefault(s.charAt(left),0)-1); //移除旧元素
                left++;
            }
        }

        return res.equals(s+"a")?"":res; //如果没有变化说明没有解,这个时候返回“”,如果有变化说明有解存在,返回真正的解
    }

    /** 判断当前是否为解 */
    public static boolean isEqual(HashMap<Character,Integer> Smap,HashMap<Character,Integer> Tmap){

        for(char chTmp:Tmap.keySet()){

            if(Tmap.getOrDefault(chTmp,0)>Smap.getOrDefault(chTmp,0)) return false;
        }
        return true;
    }
}
总结:

这道题其实难度等级是 困难,但是它和其他滑动窗口的题型似乎又没有太太差别,但是因为所给例子多了,很容易出现超出时间限制的问题,就更考验代码执行优化的能力。

大家好,我是二十一画,感谢您的品读,如有帮助,不胜荣幸~😊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值