滑动窗口法用于求满足某种条件的最短或最长子数组(子串)如:
1)最小摘要
2)sum大于target的最短子数组
3)最长的无重复字符的子串
4)最长的最多有k个不同字符的子串
5)leet1004 最大连续1的个数III
6) leet1493删掉一个元素全为1的最长子数组
首先,子段问题基本框架是O(n^2)枚举加 O(n)的check判断。可以用滑动窗口法的问题的特点是,枚举时候内层循环可以不回退,因为那些解肯定没有当前找到的最优解更优。这里面也有个可行解的单调性分布问题,对于固定一个右端,可行解是单调分布的,就是找第一个失败的build问题,可行解的分布是000011111,或者11110000,前者就是最大子数组问题,后者是最小子数组问题。左边端点i的移动其实都可以二分,只不过右端枚举已经O(n)了,只是把O(n + n)变成O(n + logn),意义不大。
最短子串模板:1)外层循环遍历终点j ,2)内循环:如果当前窗口满足条件,和最最优比并保存,然后起点 i++
for (int j = 0; j < s.size(); j++) {
窗口右端扩展,加进s[j], 更新条件
while(满足条件) {
和当前最优比较并保存
窗口左端移除s[i],更新条件,i++
}
}
最长子串模板:当前窗口满足条件,1)外循环加进一个元素,2)内循环:如果破坏了invariant,窗口左指针不断推进以满足条件,3)内循环退出:满足条件,和最优比较并记录。
for (int j = 0; j < s.size(); j++) {
窗口右端扩展,加进s[j], 更新条件
while(不满足条件) {
窗口左端移除s[i],更新条件,然后i++
}
此时重新满足条件,和最优比较并记录
}
区别主要是:最短子串模板因为求最短,所以在窗口缩小的时候记录,最长子串模板是在窗口扩展时候记录
最长无重复子串 和leetcode 1004最大连续1的个数III 以及leet1493删掉一个元素全为1的最长子数组,可以看作是最长子串的模板的简单情况:当新的右端j加入窗口后,如果不满足窗口条件,i直接能跳到使得窗口条件满足的位置,无需 while(不满足条件)i++ 一点一点的移动左端。
1 最短摘要:求s种包含t中所有字符的最短子串
string minWindow(string s, string t) {
vector<int> table(256, 0), count(256, 0);
for (char c : t) table[c]++;
int i = 0, num = 0, minLen = s.size() + 1, minStart = -1;
for (int j = 0; j < s.size(); j++) {
if (table[s[j]] == 0) continue;
if (count[s[j]] < table[s[j]]) num++;
count[s[j]]++;
for (; num == t.size(); i++) {
if (j - i + 1 < minLen) {
minLen = j - i + 1;
minStart = i;
}
count[s[i]]--;
if (table[s[i]] > 0 && count[s[i]] < table[s[i]]) num--;
}
}
return minStart >= 0 ? s.substr(minStart, minLen) : "";
}
2 和大于给定值的最小子数组,(均为正数)
def minSubArrayLen(self, s, nums):
i, sum, minLen = 0, 0, len(nums) + 1
for j in xrange(len(nums)):
sum += nums[j]
while sum >= s:
minLen = min(minLen, j - i + 1)
sum -= nums[i]
i += 1
if minLen > len(nums): return 0
else: return minLen
3最长无重复字符子串:
int lengthOfLongestSubstring(string s) {
vector<int> charSet(256, -1);
int maxLen = 0;
for (int start = 0, i = 0; i < s.size(); i++) {
if (charSet[s[i]] >= start) start = charSet[s[i]] + 1;
charSet[s[i]] = i;
maxLen = max(maxLen, i - start + 1);
}
return maxLen;
}
4最长的只有k个不同字符的子串:
int lengthOfLongestSubstringKDistinct(string s, int k) {
vector<int> lastPosition(256, -1);
int numChars= 0, maxLen = 0;
for (int start = 0, i = 0; i < s.size(); i++) {
if (lastPosition[s[i]] < start) numChars++;
lastPosition[s[i]] = i;
while (numChars > k) {
if (lastPosition[s[start]] == start) numChars--;
start++;
}
maxLen = max(maxLen, i - start + 1);
}
return maxLen;
}
5 最大连续1的个数III
6 leet1493删掉一个元素全为1的最长子数组
class Solution {
public int longestSubarray(int[] nums) {
int ans = 0;
for (int j = 0, start = 0, p = -1; j < nums.length; ++j) {
if (nums[j] == 0) {
if (p >= 0) start = p + 1;
p = j;
}
ans = Math.max(ans, j - start + 1);
}
return ans - 1;
}
}