76. 最小覆盖子串
思路:给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内,从字符串 S 里面找出:包含 T 所有字符的最小子串。
输入:S = "ADOBECODEBANC", T = "ABC"
输出:"BANC"
这一题可以采用双指针,right指针不断向右移动,直到框住满足条件的字符串,然后这时候移动left指针缩小框住的范围即可,直到right遍历完所有字符串。所以这一题关键的一点是如何判断框住的字符串是满足条件的,这里我采用的是两个哈希表,其实也可以使用数组,但是我更喜欢使用哈希表,不过等下就可以看到,哈希表可能是存在问题的。
我写出来的第一个版本是这样的:
public String minWindow(String s,String t){
if(s.length() == 0 || t.length() > s.length()) return "";
Map<Character,Integer> map = new HashMap<>();
for(int i=0;i<t.length();i++){
map.put(t.charAt(i),map.getOrDefault(t.charAt(i),0) + 1);
}
int count = 0;
Map<Character,Integer> map1 = new HashMap<>();
int left = 0;
int right = 0;
int start = -1;
int len = Integer.MAX_VALUE;
while(right < s.length()){
char tmp = s.charAt(right);
right++;
if(map.containsKey(tmp)){
map1.put(tmp,map1.getOrDefault(tmp,0) + 1);
if(map.get(tmp) == map1.get(tmp)) count++;
}
while(count == t.length()){
if(len > right - left){
len = right - left;
start = left;
}
char tmp1 = s.charAt(left);
if (map.containsKey(tmp1)){
if (map.get(tmp1) == map1.get(tmp1)){
count--;
}
map1.put(tmp1,map1.get(tmp1)-1);
}
left++;
}
}
return len == Integer.MAX_VALUE ? "" : s.substring(start,start+len);
}
结果报错,原因是count == t.length()这个判断出现了问题,不能通过这个判断来确定是否框住的字符串满足条件,因为t字符串中可能是有重复字符的!!应该修改为count == map.size()。
但是修改后仍然出错,最后一个很长的测试用例无法通过,为什么呢?
首先Integer是对象,Integer会缓存频繁使用的数值,数值范围为-128到127时直接返回缓存值,可以用==判断,但是超过该范围就会new一个对象,那么应该用equals判断。这里最简单的解决方法是,直接转成int类型。
public String minWindow(String s,String t){
if(s.length() == 0 || t.length() > s.length()) return "";
Map<Character,Integer> map = new HashMap<>();
for(int i=0;i<t.length();i++){
map.put(t.charAt(i),map.getOrDefault(t.charAt(i),0) + 1);
}
int count = 0;
Map<Character,Integer> map1 = new HashMap<>();
int left = 0;
int right = 0;
int start = -1;
int len = Integer.MAX_VALUE;
while(right < s.length()){
char tmp = s.charAt(right);
right++;
if(map.containsKey(tmp)){
map1.put(tmp,map1.getOrDefault(tmp,0) + 1);
if(map.get(tmp).intValue() == map1.get(tmp).intValue()) count++;
}
while(count == map.size()){//count == t.length()
if(len > right - left){
len = right - left;
start = left;
}
char tmp1 = s.charAt(left);
if (map.containsKey(tmp1)){
if (map.get(tmp1).intValue() == map1.get(tmp1).intValue()){
count--;
}
map1.put(tmp1,map1.get(tmp1)-1);
}
left++;
}
}
return len == Integer.MAX_VALUE ? "" : s.substring(start,start+len);
}
然后我写的第二遍,又错了。。。错误原因已经写在代码中了:
public String minWindow(String s,String t){
if(s.length() == 0 || t.length() > s.length()) return "";
Map<Character,Integer> map = new HashMap<>();
for(int i=0;i<t.length();i++){
map.put(t.charAt(i),map.getOrDefault(t.charAt(i),0) + 1);
}
int count = 0;
Map<Character,Integer> map1 = new HashMap<>();
int left = 0;
int right = 0;
while(right < s.length()){
while(right < s.length() && count < map.size()){
map1.put(s.charAt(right),map1.getOrDefault(s.charAt(right),0) + 1);
if(map.containsKey(s.charAt(right)) && map1.get(s.charAt(right)).intValue() == map.get(s.charAt(right)).intValue()) count++;
right++;
}
//错误原因s = "ADOBECODEBANC", t = "ABC",right卡在"ADOBEC"末端,无法退出循环,并且也没有移动right,此时"ADOBEC"也不是最短
if(count == map.size()){//没有记录最短
while(left <= right){
if(map1.get(s.charAt(left)) > map1.get(s.charAt(left))){
map1.put(s.charAt(left),map1.get(s.charAt(left))-1);
left++;
}else{
break;
}
}
}
}
return s.substring(left,right);
}
可以这样改:
public String minWindow(String s,String t){
if(s.length() == 0 || t.length() > s.length()) return "";
Map<Character,Integer> map = new HashMap<>();
for(int i=0;i<t.length();i++){
map.put(t.charAt(i),map.getOrDefault(t.charAt(i),0) + 1);
}
int count = 0;
Map<Character,Integer> map1 = new HashMap<>();
int left = 0;
int right = 0;
int len = Integer.MAX_VALUE;
int[] res = new int[]{0,0};
while(right < s.length()){
while(right < s.length() && count < map.size()){
map1.put(s.charAt(right),map1.getOrDefault(s.charAt(right),0) + 1);
if(map.containsKey(s.charAt(right)) && map1.get(s.charAt(right)).intValue() == map.get(s.charAt(right)).intValue()) count++;
right++;
}
while(count == map.size()){
if(right - left < len){
res[0] = left;
res[1] = right;
len = right - left;
}
if (map.containsKey(s.charAt(left))){
if (map.get(s.charAt(left)).intValue() == map1.get(s.charAt(left)).intValue()){
count--;
}
map1.put(s.charAt(left),map1.get(s.charAt(left))-1);
}
left++;
}
}
return s.substring(res[0],res[1]);
}