给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。
示例:
输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”
说明:
如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
算法思路:
1、设置两个字符哈希数组,map_s与map_t,map_s代表当前处理的窗口区间中的字符数量,map_t代表子串T的字符数量
2、设置两个指针(记作指针i与指针begin)指向字符串中第一个字符;
3、i指针向后逐个扫描字符串中的字符,在这个过程中,循环检查begin指针是否可以向前移动:
如果当前begin指向的字符,T中没有出现,直接前移begin
如果begin指向的字符T中出现了,但是当前区间窗口中该字符数量足够,向前移动begin,并更新map_s;
否则不能移动begin,跳出检查
4、指针i每向前扫描一个字符,即检查一下是否可以更新最终结果(找到最小的包含T中所有字符的窗口)
在整个过程中,使用begin与i维护一个窗口,该窗口中的子串满足题目条件(包含T中所有字符),窗口线性向前滑动,整体复杂度为O(n)
begin指针的前移条件:
前进条件1:指向了没有在T中出现的字符
前进条件2:指向的字符出现在T中,但窗口内有超过T中该字符个数的字符
代码如下:
class Solution {
public:
string minWindow(string s, string t) {
int map_t[128] = { 0 }; //记录t字符串各字符个数
int map_s[128] = { 0 }; //记录s字符串各字符的个数
std::vector<int> vec_t; //记录t字符串中有哪些字符
for (int i = 0; i < t.length(); i++) {
map_t[t[i]]++;
}
for (int i = 0; i < 128; i++) {
if (map_t[i] > 0) {
vec_t.push_back(i);
} //遍历,将字符串t中出现的字符储存到vec_t中
}
int window_begin = 0; //最小窗口起始指针
std::string result; //最小窗口对应的字符串
for (int i = 0; i < s.length(); i++){ //i代表了窗口的尾指针
map_s[s[i]]++; //将尾指针指向的字符添加到表示窗口的map中
while (window_begin < i){ //窗口头指针不能超过尾指针
char begin_ch = s[window_begin];
if (map_t[begin_ch] == 0){ //如果当前头指针指向的字符没有在字符串中出现
window_begin++; //窗口头指针前移
}
else if (map_s[begin_ch] > map_t[begin_ch]){
map_s[begin_ch]--; //头指针前移了,它指向的字符减少1
window_begin++; //窗口头指针前移
}
else{
break;
}
}
if (is_window_ok(map_s, map_t, vec_t)){
int new_window_len = i - window_begin + 1;
if (result == "" || result.length() > new_window_len){ //结果字符串为空,或者当前窗口字符串更小的时候更新结果
result = s.substr(window_begin, new_window_len); //替换窗口对应的字符串
//s.substr(start, length),截取一个从指定位置开始,得到指定长度的字符串
}
}
}
return result;
}
private:
bool is_window_ok(int map_s[], int map_t[], std::vector<int>& vec_t) {
for (int i = 0; i < vec_t.size(); i++) { //利用vec_t遍历t中出现的字符
if (map_s[vec_t[i]] < map_t[vec_t[i]]) { //如果s出现该字符的数量小于t中出现该字符的数量
return false;
}
}
return true;
}
};