窗口有關算法題目

窗口算法

窗口算法是我自己起的不嚴謹的名字,因爲最近做了幾個leetcode中幾個算法題目,發現其算法的都需要通過維護一個窗口來實現,說到窗口,我們肯定會想到TCP/IP中的滑動窗口協議,其實這類算法題目和這個有點神似的。

滑動窗口協議是用來改善吞吐量的一種技術,TCP中採用滑動窗口來進行傳輸控制,滑動窗口的大小意味着接收方還有多大的緩衝區可以用於接收數據。——百度百科

現在我彙總一下,分析一下這種類型題目的思路。


最長無重複字符的子串

該題目原文是:“Given a string, find the length of the longest substring without repeating characters.”

思路很簡單,維護一個窗口,窗口的大小表示當前的無重複的字符串的長度,用一個變量來記錄窗口的起始位置,遍歷字符串的字符,遇到一個字符,就需要判斷該字符串是否出現過

  • 沒出現過,那麼直接記錄該字符在字符串中索引
  • 出現過,判斷上一次該字符出現的位置;該位置加1後與窗口的起始位置比較,取兩者中的最大值作爲新窗口的起始位置。

然後每次計算現在窗口的大小,不斷更新窗口的歷史最大值。代碼如下:

int lengthOfLongestSubstring(string s) 
{
    vector<int> charIndex(256, -1);
    int longest = 0, m = 0;

    for (int i = 0; i < s.length(); i++) {
        m = max(charIndex[s[i]] + 1, m);    // 確定窗口的起始位置
        charIndex[s[i]] = i;
        longest = max(longest, i - m + 1);
    }

    return longest;
}

最小窗口子串

題目原文是:“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中包含T的所有的字符的最短子串的長度。

思路的核心也是窗口,把所有包含T中字符的窗口(子串)大小求出來,取其中的最小值就可以。如何求得窗口範圍,這個題目稍微麻煩了些。

我們需要把T中出現的字符存到一個hashmap中(C++11提供了unordered_map),以便我們快速訪問。由於此題目的特殊性都是字符,我們可以提供一個大小爲256的數組來充當hashmap.

  • 然後從S的字符串的開頭開始遍歷,尾指針不斷往後掃,遇到一個字符,判斷是否在T中出現過,若出席過,count++,直到count==T.size().
  • 這時候然收縮頭指針,直到不能再收縮爲止,此時找到該段包含T中所有字符的最小窗口。之後繼續尾指針繼續後掃,遍歷S串的下一個字符,重複以上的步驟,直到結束。過程中保存歷史最小窗口。
    代碼參考了一下網上的,如下:
string minWindow(string S, string T) {
        int sLen=S.size();
        int tLen=T.size();
        if(tLen==0 || sLen<tLen) return "";

        int needFind[256]={0};
        int hasFind[256]={0};

        for(int i=0;i<tLen;i++)
        {
            needFind[T[i]]++;
        }

        int minWindowLength=INT_MAX;
        int minBegin=0;
        int minEnd=sLen-1;
        int begin=0;
        int end=0;
        for(int count=0;end<sLen;end++)
        {
            //T中沒有出現的字符
            if(needFind[S[end]]==0) continue;

            hasFind[S[end]]++;
            //若是超過了T中字符出現的次數,就不需要count++
            if(hasFind[S[end]] <= needFind[S[end]])
                count++;

            //a window exists from begin to end
            if(count==tLen)
            {
                //不斷移動頭指針來找到最小窗口
                while(begin<end)
                {
                    if(needFind[S[begin]]==0) 
                    {
                        begin++;
                        continue;
                    }
                    if(hasFind[S[begin]] > needFind[S[begin]])
                    {
                        hasFind[S[begin]]--;
                        begin++;
                        continue;
                    }
                    else
                        break;
                }

                int tmpWindowLength=end-begin+1;

                if(tmpWindowLength < minWindowLength)
                {
                    minBegin=begin;
                    minEnd=end;
                    minWindowLength=tmpWindowLength;
                }
            }
        }

        if(minWindowLength==INT_MAX)
            return "";
        return S.substr(minBegin,minWindowLength);
    }

我記得還有一個類似的題目,但是想不起來了。
總的來說這類題目需要我們維護一個窗口,可以通過記錄窗口的大小,頭指針,尾指針來維護,而且一般窗口中包含的元素需要滿足題意中某些條件,一般來說就是包含了某某的全部的元素。
這類題目不難,關鍵在於想到如何來不斷調整窗口的大小,頭尾指針,想明白了這點,問題就基本可以解決哈。

發佈了42 篇原創文章 · 獲贊 32 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章