
这道题目只写出来一半,我想着思路和之前那个无重复字符的最长子串有点像,然后就依葫芦画瓢写了一部分,但是还是没有想到额外定义一个左右指针来维护最短子串的左右端点的下标,这里把这个思路具体说下。
首先我们依然需要定义两个哈希表hash_T
和hash_S
,其中hash_T
用于统计字符串t中各个字符的分布情况,而hash_S
用于统计滑动窗口内的字符分布情况。首先,不管字符串s
能否涵盖字符串t
,我们都默认s
能涵盖t
,因此我们定义result_left
和result_right
分别代表最终的最短子串的起点下标和终点下标,在初始状态下,令result_left = -1
,result_right = s.size()
,然后我们再定义左右指针left
和right
,两个指针在初始状态下都指向s
的第一个字符,然后使用for
循环用右指针遍历s
中的字符,然后我们先将右指针指向的字符计入hash_S
,然后判断hash_S
是否涵盖了hash_T
(hash_T
中的每一种字符hash_S
中都要有,而且hash_T
中每一种字符的个数不大于hash_S
中对应的字符个数),如果涵盖了,就说明当前滑动窗口已经找到了符合要求的子串,如果该子串的长度(right - left
)小于上一个符合要求的子串的长度(result_right - result_left
)直接将left
赋值给result_left
,将right
赋值给result_right
,这就实现了结果的更新。注意,在更新结果后需要及时将left
指向的字符的数量-1
,并将left
右移。只要hash_S
涵盖了hash_T
,就一直循环记录结果,当不再涵盖时,再进行下一次for
循环。当外层的for
循环结束后,我们需要判断s是否真的满足涵盖条件,如果自始至终都不满足,那么result_left
将一直指向-1
,此时直接返回空字符串即可,否则就返回s字符串中[result_left, result_right]
区间范围内的子串。
cpp
class Solution {
public:
//判断
bool is_covered(unordered_map<char, int>& hash_T, unordered_map<char, int>& hash_S){
for(pair<const char, int>& p : hash_T){
if(!hash_S.contains(p.first) || hash_S[p.first] < hash_T[p.first])
return false;
}
return true;
}
string minWindow(string s, string t) {
int result_left = -1, result_right = s.size();
unordered_map<char, int> hash_T; //用来存储字符串t的字符分布情况
unordered_map<char, int> hash_S; //用来存储子串内的字符分布情况
for(char& c : t)
hash_T[c]++;
for(int left = 0, right = 0; right < s.size(); right++){
hash_S[s[right]]++;
while(is_covered(hash_T, hash_S)){ //当t涵盖s时执行循环
if(right - left < result_right - result_left){ //寻找到更短的子串
result_left = left;
result_right = right;
}
hash_S[s[left]]--;
left++;
}
}
return result_left < 0 ? "" : s.substr(result_left, result_right - result_left + 1);
}
};