题目
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串"" 。
注意:
- 对于
t中重复字符,我们寻找的子字符串中该字符数量必须不少于t中该字符数量。 - 如果
s中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC” 输出:“BANC”
解题思路:双指针滑动窗口
本问题要求我们返回字符串 中包含字符串
的全部字符的最小窗口。我们称包含
的全部字母的窗口为「可行」窗口。
我们可以用滑动窗口的思想解决这个问题。在滑动窗口类型的问题中都会有两个指针,一个用于「延伸」现有窗口的 指针,和一个用于「收缩」窗口的
指针。在任意时刻,只有一个指针运动,而另一个保持静止。我们在
上滑动窗口,通过移动
指针不断扩张窗口。当窗口包含
全部所需的字符后,如果能收缩,我们就收缩窗口直到得到最小窗口。
![[NC]28. 最小覆盖子串 - 图9](https://cdn.nlark.com/yuque/0/2022/gif/2955945/1647396688436-a018cf76-a0ad-408e-9fec-5580320d7aef.gif#clientId=u8d969df1-4af2-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u5cf007e3&margin=%5Bobject%20Object%5D&originHeight=480&originWidth=1080&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u240f0805-fd86-4d5e-b037-97b8903d3b6&title=)
注意:这里 **t** 中可能出现重复的字符,所以我们要记录字符的个数。
考虑如何优化?
如果 ,
,那么显然
是第一个得到的「可行」区间,得到这个可行区间后,我们按照「收缩」窗口的原则更新左边界,得到最小区间。我们其实做了一些无用的操作,就是更新右边界的时候「延伸」进了很多无用的
,更新左边界的时候「收缩」扔掉了这些无用的
,做了这么多无用的操作,只是为了得到短短的
。因此我们需要在最开始的时候对于临时表为空,且不在字串中的字符进行丢弃,将
这情况的无用的
全部丢弃。
复杂度分析
时间复杂度:
- 开始需要对  中的元素都执行一次插入,最坏情况下左右指针对  的每个元素各遍历一遍,每次检查是否可行会遍历整个  的哈希表,哈希表的大小与字符集的大小有关,设字符集大小为  ,则渐进时间复杂度为  。
空间复杂度:
- 这里用了两张哈希表作为辅助空间,每张哈希表最多不会存放超过字符集大小的键值对,我们设字符集大小为  ,则渐进空间复杂度为  。
我的代码
public class Solution {public boolean CharEnoughInT( HashMap<Character,Integer>submap, HashMap<Character,Integer>tempmap){// 字符都不满足if(submap.size()>tempmap.size()) return false;// entry 是子串必须要具备的for(Map.Entry<Character,Integer> entry:submap.entrySet()){// 字串当前字符个数多于临时串说明不匹配if(entry.getValue()>tempmap.getOrDefault(entry.getKey(),0))return false;}return true;}public String minWindow (String S, String T) {// write code hereHashMap<Character,Integer>submap=new HashMap<>(); // 记录T的哈希表HashMap<Character,Integer>tempmap=new HashMap<>(); // 记录临时串的哈希表// 将字串的哈希打表for(int i=0;i<T.length();i++){char c=T.charAt(i); // 得到T的当前字符submap.put(c,submap.getOrDefault(c,0)+1);}// 双指针int l=0,r=0;int resL=0,resR=Integer.MAX_VALUE;// 遍历右指针for(r = 0;r<S.length();r++){char c=S.charAt(r); // 得到S的当前字符// 如果临时字符是空的,当前字符还不在字串中那么可以lr直接跳过if(tempmap.size()==0 && !submap.containsKey(c)){l=r+1;continue;}// 插入当前字符串,由于不清楚是否存在先查,查到原基础+1,查不到设置1tempmap.put(c,tempmap.getOrDefault(c,0)+1);// 此时临时字符串不是空的,判断是否满足条件临时串覆盖了字串while(CharEnoughInT(submap,tempmap)){// 满足字串,查看能否刷新长度if((resR-resL)>(r-l)){resL=l;resR=r;}// l指针移动,并移除当前l的字符char Slc=S.charAt(l); // 获取l指针在S串中的字符int SlcTempnum=tempmap.get(Slc); // 得到临时map中该字符的数量tempmap.put(Slc,SlcTempnum-1); // 将当前字符数-1if(SlcTempnum==0) tempmap.remove(Slc); // 如果当前字符数为0,移除出mapl++;}}if(resR==Integer.MAX_VALUE) return "";return S.substring(resL,resR+1);}}
