一、题目
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
For example,
S = "ADOBECODEBANC"
T = "ABC"
Minimum window is "BANC"
.
Note:
If there is no such window in S that covers all characters in T, return the empty string""
.
If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.
题意:给你两个字符串S,T,在S中查找子串,子串应该包含T中所有元素,这里不需要两者相等是包含的关系,保证最终返回的是符合条件长度最小的子串。注意:同样字符串的题,首先考虑字符集的范围,T大于S特殊情况,如果长度最小的有多个该如何返回,无解应该返回什么。
这个题就厉害了,在leetcode中也是hard类型的,在时间复杂度O(n)情况下我是没想出来。
思路:滑动窗口。以下代码是一位大佬的,实体就10行,这里做了转换方便阅读。
关于map的更新:在map内下标就为对应字母的ASCII,当map中为正数表示窗口缺少这几位元素,当为0表示缺少的这几位元素已经加载了,当为负数表示已经含有该元素多少个视负值而定。代码注释根据自己的理解写了下,表述不当的地方多多包涵。
核心:窗口如何去移动。
1.起始窗口[begin...end]的长度为0,保持begin不动,移动end直到第一次窗口内含有t串中所有元素,更新此时的子串长度d。在移动过程中,出现t串中元素值为0,非t串元素为负(负多少根据进入窗口内数量有关)。
2.看begin位置是否是t串中元素,即看该位置的map值是否为0。若不是,移动begin表示该元素移除窗口,其map值进行++,直到出现t串中元素,count++,该元素的map为正表示窗口中还缺这一元素,从end之后查找是否有该元素。
3.窗口实现的效果就是在s串中去找含t串中所有元素出现过的子串,并将最小的记录下来。
class Solution {
public:
string minWindow(string s, string t) {
vector<int> map(128, 0);
for (auto c : t) map[c]++; //统计t串中各个字母出现的频率,方便查表
int counter = t.size(), begin = 0, end = 0, d = INT_MAX, head = 0; //t串的个数,窗口的起点,窗口的终点,窗口的默认的长度,窗口起始的元素的索引
while (end < s.size()){ //遍历一遍s串
//if ((map[s[end++]]--) > 0) counter--; //in t
if (map[s[end]]>0) //窗口右侧挨个查找s串中元素是否在t中
{
counter--; //窗口已有t串中一个元素还需count--个
}
map[s[end]]--; //更新map表示该元素已经在窗口内
end++; //移动窗口右侧
while (counter == 0){ //valid //上边的循环直到窗口内含有t串中所有元素
if (end - begin < d)
{
d = end - (head = begin); //更新满足条件子串的长度
}
//if (map[s[begin++]]++ == 0) counter++; //make it invalid
if (map[s[begin]] == 0) //当窗口左侧为t串中元素时,将该元素移除窗口,让右侧窗口不断的去查找该元素,直到找到为止
{
counter++; //make it invalid
}
map[s[begin]]++; //不断的移动左侧直到t串中有元素出现,而且该元素必须是窗口内含有的
begin++;
}
}
return d==INT_MAX? "":s.substr(head, d);
}
};