题目链接:最小覆盖子串
题目描述:
给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内,从字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入:S = “ADOBECODEBANC”, T = “ABC”
输出:“BANC”
提示:
如果 S 中不存这样的子串,则返回空字符串 ""。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
题目分析:双指针加滑动窗口。在S中用i ,j 维护一个滑动窗口,j不断向右延申,一旦窗口中的值包含T中所有字符时,我们就开始收缩左边界i,直到窗口中的值不再包含T的所有字符,在收缩i的过程中同时不断更新ans。那么我们如何判断窗口是否包含T的所有字符呢?方法是使用Map计数,对每个字符出现的次数计数,注意这里不能直接用set去做判断,因为T中一个字符可能出现多次。用一个变量cnt,当窗口中的值为T包含字符时,cnt++,因此当cnt == T.length时,此时就包含了T所有字符,然后开始收缩指针i,收缩时如果碰到T中包含的字符,同时要将cnt–,这样才能保证窗口中的值包含T中字符的个数是准确的。看代码吧。
代码:
class Solution {
public String minWindow(String s, String t) {
int[] windows = new int[129];
int[] need = new int[129];
char[] tt = t.toCharArray();
char[] ss = s.toCharArray();
for(int i = 0;i < tt.length;i++) need[tt[i]]++;
int i = 0, j = 0,min = s.length(),cnt = 0;
String ans = "";
while(j < ss.length){
char c = ss[j];
windows[c]++;
if(windows[c]<=need[c]) cnt++;
while(cnt == tt.length){//包含T全部了,开始收缩
if(j-i+1<=min){
min = j-i+1;
ans = s.substring(i,j+1);
}
windows[ss[i]]--;
if(windows[ss[i]]<need[ss[i]]) cnt--;//维持窗口中T的字符出现次数
i++;
if(i>=ss.length) break;
}
j++;
}
return ans;
}
}
变式:求解一个字符串S的包含它自身所有字符的最短子串。
分析:完全就是上面的思路,只需要先利用set先求出S的所有字符组成的字符串,即T,然后套着上题就行了