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).
自然的想法是从左向右扫,将S[i]加入window。当window满足条件时,尝试从window头踢字符(为了使window最小),然后每次取最小值。
问题的难点就是判断当前window是否包含T的所有字符。然后想到的是看做两个集合的包含关系的判断:
1. 用两个bitset做。后来发现这样只能判断是否含有T的所有字符种类。卒~
2. 用两个map做。当时这样做很慢,180ms~
其实在做法2的基础上改进很多。例如我们可以只用一个map,扫描T时map[T[i]] -= 1
;扫描S时,map[S[i]] += 1
。当map的每个value都大于等于0时,则表明当前window是包含T的所有字符的。进一步地,可以设置一个counter变量来记录window比T少的字符个数,然后每次检查counter就可以得知window是否合法。具体见代码二。
//代码一
class Solution {
public:
inline bool isContain(map<char, int>& ss, map<char, int>& tt){
for (auto it = tt.begin(); it != tt.end();++it){
if (ss[(it->first)] < (it->second))
return false;
}
return true;
}
string minWindow(string s, string t) {
if (s.size() < t.size() || s.empty() || t.empty()) return "";
map<char, int> ss, tt;
for (char &c : t){
++tt[c];
}
int start = 0, l = -1, r = s.size();
bool flag = false;
for (int i = 0;i < s.size();++i){
++ss[s[i]];
if (isContain(ss, tt)){
flag = true;
do{
--ss[s[start]];
++start;
}while (isContain(ss,tt));
--start;
++ss[s[start]];
if (i-start < r-l){
l = start;
r = i;
}
}
}
if (flag)
return s.substr(l, r-l+1);
else
return "";
}
};
//代码二
//由于ASCII编码只有128个字符,所以可以用一个int[128]来代替map
class Solution {
public:
string minWindow(string s, string t) {
int n=s.size();
int m=t.size();
if(n<1 || m<1 || n<m)
return string("");
int map[255]={0};
for(char c: t)
map[c]--;
int counter=-m;
int begin=0;
int end=0;
int d=n+1;
int head=0;
while(end<n)
{
if(map[s[end]]<0)
{
counter++;
}
map[s[end]]++;
end++;
while(counter==0)
{
if(d>end-begin)
{
d=end-begin;
head=begin;
}
if(map[s[begin]]==0) counter--;
map[s[begin]]--;
begin++;
}
}
if(d==n+1)
return string("");
return string(s, head, d);
}
};