題目:
給定一個字符串,請你找出其中不含有重復字符的 最長子串 的長度。
示例 1:
輸入: "abcabcbb"
輸出: 3
解釋: 因為無重復字符的最長子串是 "abc",所以其長度為 3。
子串和串中字符的對比,只要出現相同字符都算重復,肯定要用到嵌套循環。
如果暴力枚舉,每個長度的子串都拿出來做自檢,時間復雜度會比較高。
使用滑動窗口的思想,對錯誤信息進行合理利用,可以有效減少執行次數。
C語言解法:
int lengthOfLongestSubstring(char * s){ int i = 0, j = 0; int maxlen = 0; int curlen = 0; int len = strlen(s); if (len == 0) return 0; for (; j < len && len - i > maxlen; j++) { curlen++; for (int k = i; k <= j; k++) { if (s[k] == s[j + 1]) { if (curlen > maxlen) maxlen = curlen; i = k + 1; curlen = j - i + 1; break; } } } if (curlen > maxlen) return curlen; else return maxlen; }
滑動窗口初聽覺得抽象模糊,其實拿卷尺一對比很好理解
一個字符串,要在里面找出最長且沒有重復字符的子串,就像拿着卷尺在上面不停地縮拉測量
子串就是這個卷尺的伸出部分,即一個窗口,左邊縮進,右邊拉出
因為不能有重復的字符,在右端逐漸拉長的過程中,每新增加的一個新字符都要拿來和左側子串中的字符做對比
在上面的程序里,用 i 指向卷尺頭部,用 j 指向卷尺尾部,k 則作為子串中字符的索引
每次對比開始時,用 i 的值初始化 k,當找到重復字符時,又將 k + 1的值賦給 i,即直接將窗口的左側移動到重復字符的下一個字符位置
窗口右側每次向右滑動一格,如果窗口中子串包含窗口右側下一個字符,左側滑動一格或多格
與枚舉法相比,由於利用了子串中重復字符的位置,直接將窗口左側跳到該字符的下一個位置,每次檢查出重復減少了 k - i 個子串的自檢
題目
給定數組,獲取數組中n個連續元素最大的和
C語言解法:
1 int maxSumSub(nums, n) { 2 int len = (int) sizeof(nums) / sizeof(int); 3 int maxsum = 0; 4 if (n > len) { 5 return 0; 6 } 7 for (int i = 0; i < n; i++) { 8 maxsum += nums[i]; 9 } 10 11 int windowsum = maxsum; 12 13 for (int i = n; i < len; i++) { 14 windowsum += nums[i] - nums[i - n]; 15 if (windowsum > maxsum) 16 maxsum = windowsum; 17 } 18 return maxsum; 19 }
和上面的求最長無重復子串類似,如果使用枚舉法,假設數組長度為 len,則要求出 len - n + 1 個和,進行 len - n 次對比
如果用滑動窗口法,基本思想是,每次向右滑動時,窗口右側進入的新元素減去左側退出的舊元素,得到的結果與上次的和相加得到新的和
這樣做的巧妙之處在於,無論題設n有多大,每次窗口移動后,計算新和,只需將窗口兩端進出的新舊元素相減,得到的結果與舊和相加就可以得到新和
總結
上述兩個題目,分別用到了動態滑動窗口和固定滑動窗口,即在滑動時長度變化和不變的兩種窗口
可以看到,滑動窗口的應用場景有幾個特點:
1. 需要輸出或比較的結果在原數據結構中是連續排列的(字符串中的連續不重復子串,數組中的連續元素最大和)
2. 每次窗口滑動時,只需觀察窗口兩端元素的變化,無論窗口多長,每次只操作兩個頭尾元素,當用到的窗口比較長時,可以顯著減少操作次數
3. 窗口內元素的整體性比較強,窗口滑動可以只通過操作頭尾兩個位置的變化實現,但對比結果時往往要用到窗口中所有元素(檢查窗口中是否含有重復字符,對比窗口中元素的和)