滑动窗口法模板

本文详细介绍滑动窗口算法的应用场景及实现模板,包括求最短或最长子数组的多种典型问题,如最小摘要、子数组和超过目标值的最短长度等。文章通过实例讲解如何高效地解决这些问题,并提供C++代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

### 滑动窗口算法Java 实现模板 滑动窗口是一种常见的优化技术,主要用于处理数组或字符串中的子序列问题。它可以将暴力解法的时间复杂度从 \( O(n^2) \) 降低到 \( O(n) \),通过维护一个动态调整的窗口来解决问题。 以下是滑动窗口算法的两种常见实现方式: #### 1. 固定大小的滑动窗口模板 对于固定大小的滑动窗口,可以通过简单的循环遍历数组并计算窗口内的值。以下是一个通用的固定大小滑动窗口模板[^1]: ```java public class FixedSlidingWindow { public static void fixedSizeSlidingWindow(int[] nums, int windowSize) { if (nums == null || windowSize <= 0 || windowSize > nums.length) return; int windowSum = 0; // 初始化第一个窗口的和 for (int i = 0; i < windowSize; i++) { windowSum += nums[i]; } System.out.println("Initial Window Sum: " + windowSum); // 移动窗口 for (int i = windowSize; i < nums.length; i++) { windowSum = windowSum - nums[i - windowSize] + nums[i]; System.out.println("Current Window Sum: " + windowSum); } } public static void main(String[] args) { int[] nums = {1, 2, 3, 4, 5, 6}; int k = 3; fixedSizeSlidingWindow(nums, k); } } ``` 此代码展示了如何使用固定的窗口大小来计算连续子数组的和。 --- #### 2. 动态大小的滑动窗口模板 动态大小的滑动窗口适用于更复杂的场景,比如寻找满足某些条件的最小子数组长度等问题。以下是一个典型的动态滑动窗口模板[^2]: ```java class DynamicSlidingWindow { public int minSubArrayLen(int target, int[] nums) { int result = Integer.MAX_VALUE; int sum = 0; int l = 0; for (int r = 0; r < nums.length; r++) { sum += nums[r]; while (sum >= target) { result = Math.min(result, r - l + 1); sum -= nums[l++]; } } return result == Integer.MAX_VALUE ? 0 : result; } public static void main(String[] args) { DynamicSlidingWindow solution = new DynamicSlidingWindow(); int[] nums = {2, 3, 1, 2, 4, 3}; int target = 7; System.out.println(solution.minSubArrayLen(target, nums)); // 输出:2 } } ``` 在此代码中,`l` 和 `r` 分别代表窗口的左边界和右边界。通过不断移动 `l` 来缩小窗口范围,直到不再满足目标条件为止。 --- ### 总结 上述两段代码分别实现了固定大小和动态大小的滑动窗口算法。前者适合于简单的问题,而后者则更加灵活,能够应对更为复杂的约束条件。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值