最小覆盖子串
题目描述:
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。提示:1 <= s.length, t.length <= 10^5s 和 t 由英文字母组成
class Solution {
private String s;
private String t;
private Map<Character,Integer> index;
private Map<Character,Integer> window;
public String minWindow(String s, String t) {
// 初始化
int len = s.length();
if(t.length()>len) return ""; // 特判
if(len == 1){
if(t.length() == 1 && s.charAt(0) == t.charAt(0)) return ""+s.charAt(0);
else return "";
}
this.s = s;
this.t = t;
String result = "";
this.index = new HashMap<Character,Integer>();
this.window = new HashMap<Character,Integer>();
for(int i = 0 ; i<t.length() ; i++){
this.index.put(t.charAt(i),index.getOrDefault(t.charAt(i),0)+1);
}
// 双指针法
int left = 0;
int right = 0;
int minLen = len;
while(right<len){
// 首先移动右指针直至找到一个包含所有t字符的子串
while(right<len && !check()){
window.put(s.charAt(right),window.getOrDefault(s.charAt(right),0)+1);
right++;
}
// 其次移动左指针直至无法找到一个包含所有t字符串的子串
while(check()){
window.put(s.charAt(left),window.getOrDefault(s.charAt(left),0)-1); // 负数不会影响结果
left++;
}
// 处理
if(minLen >= (right-left+1)){
minLen = right-left+1;
result = s.substring(left-1,right);// left-1到right的子串
}
}
return result;
}
private boolean check(){
Iterator iter = index.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Character key = (Character) entry.getKey();
Integer val = (Integer) entry.getValue();
if (window.getOrDefault(key, 0) < val) {
return false;
}
}
return true;
}
}
该题利用双指针法,首先移动右指针,直到找到某一段s字符串含有t字符串所有字符的才停止移动,然后移动左指针,直到不满足该段s字符串中含有t字符串所有字符才停止移动,整个操作重复以上步骤,每次记录最小的result和最小长度。这里主要的难点就是:如何才能判断t字符串元素都包含在某一段s字符串中的所有元素中,笔者初次使用数组,发现卡在最后一个数据集,超时了,然后借鉴了官方的题解,利用HashMap结构,才通过的。详细请看代码,读者有疑问欢迎留言。