题目:
含有所有字符的最短字符串
给定两个字符串 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
s
和t
由英文字母组成- **进阶:**你能设计一个在
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;
}
}
总结:
这道题其实难度等级是 困难,但是它和其他滑动窗口的题型似乎又没有太太差别,但是因为所给例子多了,很容易出现超出时间限制的问题,就更考验代码执行优化的能力。
大家好,我是二十一画,感谢您的品读,如有帮助,不胜荣幸~😊