题目
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 emtpy string ""
.
If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.
思路一:O(N*M)
滑动窗口,左右边界分别是 left 和 right (分别指向S中某一在T中也存在的字符)。
(1)判断当前窗口是否包含所有的T中字符,若没有,则更新 right 到下一个字符(在T中也存在的字符);
(2)若当前窗口已经包含了所有的T中字符,比较字符串长度是否最短更新字符串;然后更新 left 到下一个字符(在T中也存在的字符);
首先用数组记录T中每一个字符出现的次数,,即为 hash[256] ,然后记录目前的窗口中T中字符出现的次数,通过比较两数组的区别来判断该窗口是否有效。
即 hasAll 函数。
class Solution {
public:
string minWindow(string S, string T) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
if(T.length()<=0) return T;
if(S.length()<=0) return S;
vector<int> hash(256,0);
for(int i=0;i<T.length();i++) hash[T[i]]++; // hash table
int start = 0; // the result: start and minlen
int minlen = S.length()+1;
int left=-1, right=0;
vector<int> count(256,0);
//get the first char
while(right<S.length() && !hash[S[right]]) right++;
if(right==S.length()) return "";
count[S[right]] ++;
left = right;
int rflag=false; // mask if the right has changed
while(right<S.length()) {
while(right<S.length() && !hash[S[right]]) right++;
if(right==S.length()) break;
if(rflag) count[S[right]] ++;
if(hasAll(T,hash,count)) {
if(right-left+1<minlen) {
start = left;
minlen = right-left+1;
}
count[S[left]]--;
left++;
while(left<=right && !hash[S[left]]) left++;
rflag=false;
if(left>right) break;
} else {
rflag=true;
right++;
}
}
if(minlen==S.length()+1)
return "";
return S.substr(start,minlen);
}
bool hasAll(string &T, vector<int> &hash,vector<int> &count) {
for(int i=0;i<T.length();i++){
if(count[T[i]]<hash[T[i]])
return false;
}
return true;
}
};
上述代码时间复杂度是 O(N*M) ,对大数据无法通过。
思路二:O(N)
上述代码是可以继续优化的,因为在判断该窗口是否有效时,我们没有必要再遍历一遍T ,即上述代码的子函数:
bool hasAll(string &T, vector<int> &hash,vector<int> &count)
可以在生成窗口的过程中,记录当前已经生成了多少个T中的字符:如果 num=T.length() ,表示该窗口有效,可以更新最优解。
可以通过大数据。
class Solution {
public:
string minWindow(string S, string T) {
int sLen = S.length();
int tLen = T.length();
// The hashtable used to record the num of letters of T in T and T in S
int needToFind[256] = {0};
for (int i = 0; i < tLen; i++)
needToFind[T[i]]++;
int hasFound[256] = {0};
// The window for result: begin and end
int minWindowLen = INT_MAX;
int minWindowBegin;
int minWindowEnd;
// The num of letters in the window we have
int count = 0;
for (int begin = 0, end = 0; end < sLen; end++) {
// skip characters not in T
if (needToFind[S[end]] == 0)
continue;
hasFound[S[end]]++;
if (hasFound[S[end]] <= needToFind[S[end]])
count++;
// if window constraint is satisfied
if (count == tLen) {
// advance begin index as far right as possible,
// stop when advancing breaks window constraint.
while (needToFind[S[begin]] == 0 || hasFound[S[begin]]>needToFind[S[begin]]) {
if (hasFound[S[begin]] > needToFind[S[begin]])
hasFound[S[begin]]--;
begin++;
}
// update minWindow if a minimum length is met
int windowLen = end - begin + 1;
if (windowLen < minWindowLen) {
minWindowBegin = begin;
minWindowEnd = end;
minWindowLen = windowLen;
} // end if
} // end if
} // end for
if(count<tLen)
return "";
else
return S.substr(minWindowBegin,minWindowLen);
}
};
其实上述代码还可以进一步优化:我们可以记录用一个 Queue 记录每次 right 停止的位置,因为 这些位置也是 left 将要停止的位置。
这样就 没必要进行 上述的 left 循环递增:
// advance begin index as far right as possible,
// stop when advancing breaks window constraint.
while (needToFind[S[begin]] == 0 || hasFound[S[begin]]>needToFind[S[begin]]) {
if (hasFound[S[begin]] > needToFind[S[begin]])
hasFound[S[begin]]--;
begin++;
}
可以直接弹出 Queue 中的一个 值作为 left 的值看是否满足条件。