窗口算法
窗口算法是我自己起的不嚴謹的名字,因爲最近做了幾個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);
}
我記得還有一個類似的題目,但是想不起來了。
總的來說這類題目需要我們維護一個窗口,可以通過記錄窗口的大小,頭指針,尾指針來維護,而且一般窗口中包含的元素需要滿足題意中某些條件,一般來說就是包含了某某的全部的元素。
這類題目不難,關鍵在於想到如何來不斷調整窗口的大小,頭尾指針,想明白了這點,問題就基本可以解決哈。