滑动窗口法模板

滑动窗口法用于求满足某种条件的最短或最长子数组(子串)如:

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;
    }
}

  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值