给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”
说明:
如果 S 中不存这样的子串,则返回空字符串 ""。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
思路和算法
本问题要求我们返回字符串 s 中包含字符串 ttt 的全部字符的最小窗口。我们称包含 t 的全部字母的窗口为「可行」窗口。
我们可以用滑动窗口的思想解决这个问题,在滑动窗口类型的问题中都会有两个指针。一个用于「延伸」现有窗口的 right 指针,和一个用于「收缩」窗口的 left 指针。在任意时刻,只有一个指针运动,而另一个保持静止。我们在 s 上滑动窗口,通过移动 right 指针不断扩张窗口。当窗口包含 t 全部所需的字符后,如果能收缩,我们就收缩窗口直到得到最小窗口。
import java.util.ArrayList;
import java.util.HashMap;
public class ZuiXiaoFuGaiZiChuan {
public static void main(String[] args) {
String a = "asdgasdewqewads";
String b = "ads";
Solution1 s = new Solution1();
System.out.println(s.minWindow(a, b));
}
}
class Solution1 {
public String minWindow(String s, String t) {
int slen = s.length();
int tlen = t.length();
if (slen == 0 || tlen == 0 || slen < tlen) {
return "";
}
char[] sc = s.toCharArray();
char[] tc = t.toCharArray();
int[] si = new int[128];
int[] ti = new int[128];
for (char c : tc) {
ti[c]++;
}
int minlen = slen + 1;
int distance = 0;
int right = 0;
int left = 0;
int begin = 0;
while (right < slen) {
if (ti[sc[right]] == 0) {
// 在子串中不存在该字母
right++;
continue;
}
if (si[sc[right]] < ti[sc[right]]) {
//精髓 避免串中出现的子串字母不是自己需要的
// 串中该字母出现的次数小于子串出现次数
distance++;
}
si[sc[right]]++;
right++;
while (distance == tlen) {
//右移过程找全了子串 再左移找最小重复子串
if (right - left < minlen) {
//找到最小覆盖子串
minlen = right - left;
begin = left;
}
if (ti[sc[left]] == 0) {
// 在子串中不存在该字母
left++;
continue;
}
if (si[sc[left]] == ti[sc[left]]) {
//满足wihle时 必然si[sc[left]] >= ti[sc[left]]
//所以si[sc[left]]--; 不能放在该条件中
//移除需要的字符
// 串中该字母出现的次数等于子串出现次数
distance--;
}
si[sc[left]]--;
left++;
}
}
if(minlen == slen+1) {
return "";
}
return s.substring(begin,begin + minlen);
}
}