LeetCode: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.

注意:如果一個字母在T中出現了k次,那么在窗口中該字母至少出現k次

 


分析:暴力解決可以在O(n^2)時間復雜度內完成:遍歷S,當碰到T中的首字母時,以該字母為窗口起始位置找到一個最小窗口包含所有T中的字符,最后取所有窗口的最小值。在暴力方法中我們做了很多重復的事情,比如S = “aabc”,T = “abc”,當我們以第一個a為窗口起始位置遍歷時,我們要判斷b、c是否在T中,當我們以第二個a為窗口起始位置遍歷時,我們還要判斷b、c是否在T中。那么怎樣避免這種重復的工作呢? 本文地址

 

可以利用兩個指針掃描(兩個指針分別為start,i),以S = “e b a d b a c c b”(忽略空格),T = “abc”為例:

                                                                            0 1 2 3 4 5 6 7 8

  1. 初始化 start = i = 0
  2. i 逐漸往后掃描S直到窗口S[start…i]包含所有T的字符,此時i = 6(字符c的位置)
  3. 縮減窗口:此時我們注意到窗口並不是最小的,需要調整 start 來縮減窗口。縮減規則為:如果S[start]不在T中 或者 S[start]在T中但是刪除后窗口依然可以包含T中的所有字符,那么start = start+1, 直到不滿足上述兩個縮減規則。縮減后i即為窗口的起始位置,此例中從e開始窗口中要依次刪掉e、b、a、d,start最后等於4 ,那么得到一個窗口大小為6-4+1 = 3
  4. start = start+1(此時窗口肯定不會包含所有的T中的字符),跳轉到步驟2繼續尋找下一個窗口。本例中還以找到一個窗口start = 5,i = 8,比上個窗口大,因此最終的最小窗口是S[4…6]

具體實現時,要用哈希表來映射T中字符以便在O(1)時間內判斷一個字符是否在T中,由於是字符緣故,可以用數組簡單的來實現;還需要一個哈希表來記錄掃描時T中的某個字符在S中出現的次數,也可以用數組實現

class Solution {
public:
    string minWindow(string S, string T) {
        // IMPORTANT: Please reset any member data you declared, as
        // the same Solution instance will be reused for each test case.
        int lens = S.size(), lent = T.size();
        int srcCnt[256] = {0};//T中每個字母的個數
        int foundCnt[256] = {0};//當前找到T中每個字母的個數
        for(int i = 0; i < lent; i++)
            srcCnt[T[i]]++;
        int hasFound = 0;//已經找到的字母數目
        int winStart = -1, winEnd = lens;//窗口的左右邊界
        //兩個指針start和i一起掃描
        for(int i = 0, start = 0; i < lens; i++)
            if(srcCnt[S[i]] != 0)
            {
                foundCnt[S[i]]++;
                if(foundCnt[S[i]] <= srcCnt[S[i]])hasFound++;
                if(hasFound == lent)
                {//找到了一個滿足的窗口
                    while(srcCnt[S[start]] == 0 ||
                          foundCnt[S[start]] > srcCnt[S[start]])
                    {
//縮減窗口
                        if(srcCnt[S[start]] != 0)
                            foundCnt[S[start]]--;
                        start++;
                    }
                    if(winEnd - winStart > i - start)
                    {
                        winStart = start;
                        winEnd = i;
                    }
                    foundCnt[S[start]]--;
                    start++;
                    hasFound--;
                }
            }
        return winStart != -1 ? S.substr(winStart, winEnd - winStart +1) : "";
    }
};

小小改進:注意到上述步驟3中縮減窗口時start要跳過不在T中的字符,如果S = “eeeeeeeeebadbaccb”(S的前面有大量的字符e不在T中),T = “abc”,這個跳轉會很費時,如果可以在第2步i掃描的過程中保存好T中字符在S中出現的位置,那么我們在縮減窗口時就不需要跳過例子中大量的e,只需要跳過b、a這些在T中存在但是不影響窗口的字符。這個可以用輔助隊列來實現。

class Solution {
public:
    string minWindow(string S, string T) {
        // IMPORTANT: Please reset any member data you declared, as
        // the same Solution instance will be reused for each test case.
        int lens = S.size(), lent = T.size();
        queue<int> Q;
        int srcCnt[256] = {0};//T中每個字母的個數
        int foundCnt[256] = {0};//當前找到T中每個字母的個數
        for(int i = 0; i < lent; i++)
            srcCnt[T[i]]++;
        int hasFound = 0;//已經找到的字母數目
        int winStart = -1, winEnd = lens;//窗口的左右邊界
        for(int i = 0; i < lens; i++)
            if(srcCnt[S[i]] != 0)
            {
                Q.push(i);
                foundCnt[S[i]]++;
                if(foundCnt[S[i]] <= srcCnt[S[i]])hasFound++;
                if(hasFound == lent)
                {//找到了一個滿足的窗口
                    int k;
                    do
                    {//縮減窗口到最小
                        k = Q.front();
                        Q.pop();
                        foundCnt[S[k]]--;
                    }
                    while(srcCnt[S[k]] <= foundCnt[S[k]]);
                    if(winEnd - winStart > i - k)
                    {
                        winStart = k;
                        winEnd = i;
                    }
                    hasFound--;
                }
            }
        return winStart != -1 ? S.substr(winStart, winEnd - winStart +1) : "";
    }
};

 

【版權聲明】轉載請注明出處:http://www.cnblogs.com/TenosDoIt/p/3461301.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM