1.我们设定有一排球,代号不知道,用x表示
2,我们可以通过盒子可以观察到球真正的代号,比如
3.如果我们要在这排球里找到包含代号1,2的两个球的最短的球串,
4、现在我们回到第2步,可以发现第一个球是1,因为盒子允许向左或向右伸缩,所以先向左扩,发现第二个球是3,此时盒子里的球是1,3,不包含1,2.
5,我们继续向左扩,发现第三个球是2 。此时盒子里是1,3,2,包含1,2,所以这个盒子里的球串符合要求,记下来。
6.接下来我们把盒子向左 缩,看一下少了一个球,是否还包含1,2,如果包含,那就找到了一个符合题目要求的更短球串,显然盒子里只有2,3,不符合要求。
7.我们继续把盒子向左扩大,看能不能找到符合要求的球串,显然,此时球盒里包含1,2,3,有包含1,2,所以此球串符合题目要求。那么接下来我们重复第六步,把盒子向左缩,看能不能找到一个符合题目要求的更短球串。
8.把盒子向左缩,盒子里包含1,2,找到了一个符合题目要求的更短球串。
9,重复第七步操作,每次找到符合要求的球串时,把盒子向左缩小,看能不能让子串变更短时也符合题目要求(包含1,2),如果不能,则把盒子向左扩大,纳入更多未知的球,让盒子里的球来满足题目要求(包含1,2球)。这里面有两个过程,一个扩是为了找到,一个缩是为了优化。
代码如下
最小覆盖子串(题意 :给定一个字符串S和T,找出S中包含T所有字符的最小子串。
注意:题目中只说明包含T中所有字符,顺序任意。例,S = “ADOBECODEBANC”,T = “ABC”,则满足题意的最小子串为“BANC”。当没有满足题意的子串时返回空字符串。)
public String minWindow(String s, String t) {//s相当于一排未知代号x的球,t相当要求包含的1,2球
Map<Character,Integer> need = new HashMap<>();
Map<Character,Integer> window = new HashMap<>();
for(Character c : t.toCharArray()){//把t记入need,并且根据代号记录该代号球的数量
need.put(c,need.getOrDefault(c,0) + 1);
}
int start = 0,left = 0,right = 0,len = Integer.MAX_VALUE,valid = 0;
while(right < s.length()){//设置盒子向左扩不得超过这排球的长度
char c = s.charAt(right);
right++;//盒子向左扩大
if(need.containsKey(c)){//向左扩后新增的那个球,如果是t里面的,也就是1,2球里面的
window.put(c,window.getOrDefault(c,0)+1);//把这个球的记下,并且代号数量加1
if(window.get(c).equals(need.get(c))){
valid++;
//!注意,如果两个记录的相同代号数量相当,那么
//valid+1.因为need,也就是记录t的,里面的球代号的数量
//是不变,但是window,也就是记录s中每个包含于t的新增球代 //号的数量,数量每次都增加一,一旦超过need里面代
//号相同球的数量,那么是不会再令need+1的,只有等于才会。这 //保证 了valid最大只能和need的长度相等
}
}
//判断窗口是否要收缩
while(valid == need.size()){//一旦相等了,就说明符合题目要求了,相当于包含1,2了
if(right - left < len){//这里到下面的操作都是符合题目要求后,盒子向左收缩来看有无 //更优解的操作,这步是记录盒子的长度和位置
len = right - left;
start = left;
}
char k = s.charAt(left);//先记录盒子向左收缩后,丢弃出盒子的那个球的信息
left++; //向左缩
if(need.containsKey(k)){//处理之前丢弃的那个球
if(need.get(k).equals(window.get(k))){
valid--;
}
window.put(k,window.getOrDefault(k,0)-1);
}
}
}
return len == Integer.MAX_VALUE ? "" : s.substring(start,start+len);
}