您的位置:首页 > 其它

LeetCode(76)Minimum Window Substring

2015-03-22 08:05 393 查看
题目如下:

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)的解法,参考官网的这篇分析,受教了。

基本思路不妨这样想:

首先,返回一个window,至少要包含target中的所有的字符。

第二,在返回的window中,要尽可能地使得它最小。

现在逐个来看上面两点怎么做到,

第一,返回一个window至少要包含target中的所有字符。

考虑到target中的string可能重复,所以实现应该用一个hash来存储target中的string的分布情况,代码中用到了int needToFound[256] = {0}。

接下来,比如在eeaaaabbcd中找寻"eec",那么显然返回的最小窗口是"eeaaaabbc",在这个最小窗口中,needToFound[e] = 2, needToFind[c] = 1;

也就是说,如果当前找到了a, d等等不在needToFound中的字符,虽然知道不会真正需要它们,但是也没法把它们去掉构成一个不连续的窗口,所以只能忽略不计继续扫下一个字符,而如果找到了下一个字符,并且下一个字符的数量还没到到needToFound需要的数量,那么就应该接下往下找。

for (int index = 0; index < S.length();++index) {
            if (needToFind[S[index]]<0) continue;
                hasFound[S[index]]++;
            if (hasFound[S[index]] <= needToFind[S[index]])
                count++;
}


第二,在返回的window中,要尽可能地使得它最小。

所以就要尽可能地去掉不需要的部分,于是,对于每一个index结尾的窗口,看看它前面能丢掉多少个字符。丢掉的方式有两种,如果这个字符根本就不在needToFound中,那么可以丢掉,或者,如果这个字符在当前窗口中的数量超过了needToFound需要的数量,也可丢掉。

while (needToFind[S[begin]] == 0 || hasFound[S[begin]] > needToFind[S[begin]]) { //NOTE2: two level logica
                    if (hasFound[S[begin]] > needToFind[S[begin]]) {
                        hasFound[S[begin]]--;
                    }
                    begin++;
                }


下面看完整的代码:

我的代码:

// 22ms
class Solution {
public:
string minWindow(string S, string T) {
int hasFound[256] = {0};
int needToFind[256] = {0};
int windowBegin = 0;
int windowEnd = 0;
int minwindowBegin = 0;
int minWindowEnd = 0;
//NOTE1: int minLength = S.length(); //minWindow("a", "a"): expected return "a", actual wrong answer "";
int minLength = INT_MAX;
string minStr = "";
for (int i =0; i < T.length(); ++i){
needToFind[T[i]]++;
}

int begin = 0, end = 0, count = 0;
//string str1 = "ADOBECODEBANC";
//string str2 = "ABC";
for (begin = 0, end = 0; end < S.length();++end) {
if (needToFind[S[end]]<0) continue;
hasFound[S[end]]++;
if (hasFound[S[end]] <= needToFind[S[end]])
count++;
if (count >= T.length()) {
while (needToFind[S[begin]] == 0 || hasFound[S[begin]] > needToFind[S[begin]]) { //NOTE2: two level logica if (hasFound[S[begin]] > needToFind[S[begin]]) { hasFound[S[begin]]--; } begin++; }
windowBegin = begin;
windowEnd = end;
if (windowEnd - windowBegin + 1 < minLength) {
minLength = windowEnd - windowBegin + 1;
windowEnd = end;
windowBegin = begin;
minStr = S.substr(windowBegin, windowEnd - windowBegin + 1);
}
}
}
return minStr;
}
};


小结提高:

1 上面代码的时间复杂是2*O(N)所以还是O(N)。虽然是for中嵌套了while,但是注意到for中的end只把输入string 遍历了一遍,而begin也只把输入string遍历了一遍,所以一共是O(N).

可以这么理解,begin和end之间的部分就是需要求得的minimum window。

其中每次遍历的时候,end的作用是,和begin构成一个粗略的window,满足所有需要的字符都在window中,但是不满足minimum。

接下来begin向右滑动,尽可能第接近end,直到不能再向右滑动为止。此时window是end取得当前值的情况下,最小的window。

每次进行外层for循坏的时候,begin都要么不变要么右移,不可能回头左移,所以这样来看,也可以理解为什么2层for + while是(N)时间复杂度。

2. 这道题目是two pointer思路的题目。 和two pointer思路类似的题目是best time to buy and sell stock, minimum window substring。两者虽然完全不是同一个领域的题目,但是表达的two pointer的思路一致。

参考资料:

1 http://articles.leetcode.com/2010/11/finding-minimum-window-in-s-which.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: