LeetCode(76)Minimum Window Substring

题目如下:

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的思路一致。



参考资料:

http://articles.leetcode.com/2010/11/finding-minimum-window-in-s-which.html


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值