当前位置: 首页 > news >正文

滑动窗口例题

本篇基于b站灵茶山艾府。

209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target

找出该数组中满足其总和大于等于 target 的长度最小的 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度 如果不存在符合条件的子数组,返回 0

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

输入:target = 4, nums = [1,4,4]
输出:1

示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        ans = len(nums) + 1  # 更新长度最小的子数组
        left = 0
        s = 0  # 记录滑动窗口内的总和
        for right, x in enumerate(nums):
            s += x  # 将右端点指向元素加入总和
            while s - nums[left] >= target:  # 如果减去左端点的值还满足条件,则放心移动
                s -= nums[left]  # 先减去再移动指针
                left += 1
            if s >= target:  # 加这个判断是避免上面的while循环一次也没有进入的时候
                ans = min(ans, right - left + 1)
        return ans if ans != len(nums) + 1 else 0


713. 乘积小于 K 的子数组

给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。

示例 1:

输入:nums = [10,5,2,6], k = 100
输出:8
解释:8 个乘积小于 100 的子数组分别为:[10]、[5]、[2]、[6]、[10,5]、[5,2]、[2,6]、[5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于 100 的子数组。

示例 2:

输入:nums = [1,2,3], k = 0
输出:0

class Solution:
    def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
        if k <= 1:
            return 0
        prod = 1  # 计算滑动窗口内的乘积
        left = 0
        ans = 0
        for right, x in enumerate(nums):
            prod *= x
            while prod >= k:
                prod /= nums[left]
                left += 1  # 缩小左端点
            ans += right - left + 1  # 左端点一直到右端点的子数组都是满足条件的
        return ans


3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 此题不断将右端点指向的字符存进哈希表,如果碰到出现重复元素,则移动左端点,直到无重复字符。
        from collections import Counter

        c = Counter()  # 统计窗口内字符出现个数
        left = 0
        ans = 0
        for right, x in enumerate(s):
            c[x] += 1
            while c[x] > 1:
                c[s[left]] -= 1
                left += 1
            ans = max(ans, right - left + 1)
        return ans


2958. 最多 K 个重复元素的最长子数组

给你一个整数数组 nums 和一个整数 k

一个元素 x 在数组中的 频率 指的是它在数组中的出现次数。

如果一个数组中所有元素的频率都 小于等于 k ,那么我们称这个数组是 数组。

请你返回 nums最长好 子数组的长度。

子数组 指的是一个数组中一段连续非空的元素序列。

示例 1:

输入:nums = [1,2,3,1,2,3,1,2], k = 2
输出:6
解释:最长好子数组是 [1,2,3,1,2,3] ,值 1 ,2 和 3 在子数组中的频率都没有超过 k = 2 。[2,3,1,2,3,1] 和 [3,1,2,3,1,2] 也是好子数组。
最长好子数组的长度为 6 。

示例 2:

输入:nums = [1,2,1,2,1,2,1,2], k = 1
输出:2
解释:最长好子数组是 [1,2] ,值 1 和 2 在子数组中的频率都没有超过 k = 1 。[2,1] 也是好子数组。
最长好子数组的长度为 2 。

示例 3:

输入:nums = [5,5,5,5,5,5,5], k = 4
输出:4
解释:最长好子数组是 [5,5,5,5] ,值 5 在子数组中的频率没有超过 k = 4 。
最长好子数组的长度为 4 。

class Solution:
    def maxSubarrayLength(self, nums: List[int], k: int) -> int:
        # 此题本质和上一道一样
        from collections import Counter

        c = Counter()
        left = 0
        ans = 0
        for right, x in enumerate(nums):
            c[x] += 1
            while c[x] > k:
                c[nums[left]] -= 1
                left += 1
            ans = max(ans, right - left + 1)
        return ans


2730. 找到最长的半重复子字符串

给你一个下标从 0 开始的字符串 s ,这个字符串只包含 09 的数字字符。

如果一个字符串 t 中至多有一对相邻字符是相等的,那么称这个字符串 t半重复的 。例如,"0010""002020""0123""2002""54944" 是半重复字符串,而 "00101022" (相邻的相同数字对是 00 和 22)和 "1101234883" (相邻的相同数字对是 11 和 88)不是半重复字符串。

请你返回 s 中最长 半重复 子字符串的长度。

示例 1:

输入: s = “52233”

输出: 4

解释:

最长的半重复子字符串是 “5223”。整个字符串 “52233” 有两个相邻的相同数字对 22 和 33,但最多只能选取一个。

示例 2:

输入: s = “5494”

输出: 4

解释:

s 是一个半重复字符串。

示例 3:

输入: s = “1111111”

输出: 2

解释:

最长的半重复子字符串是 “11”。子字符串 “111” 有两个相邻的相同数字对,但最多允许选取一个。


class Solution:
    def longestSemiRepetitiveSubstring(self, s: str) -> int:
        left = 0
        same = 0  # 记录窗口内相邻字符相等的对数
        ans = 0
        for right, x in enumerate(s):
            if right > 0 and s[right - 1] == x:
                same += 1
            while same > 1:
                if s[left + 1] == s[left]:  # 要将第一对相邻字符中的左边一个移除窗口
                    same -= 1
                left += 1
            ans = max(ans, right - left + 1)
        return ans


1004. 最大连续1的个数 III

给定一个二进制数组 nums 和一个整数 k,假设最多可以翻转 k0 ,则返回执行操作后 数组中连续 1 的最大个数

示例 1:

输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。

示例 2:

输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。

class Solution:
    def longestOnes(self, nums: List[int], k: int) -> int:
        left = 0
        ans = 0
        for right, x in enumerate(nums):
            if x == 0:
                k -= 1
            while k < 0:  # 当k的次数不够,说明滑动窗口内多翻转了一个
                if nums[left] == 0:
                    k += 1
                left += 1
            ans = max(ans, right - left + 1)
        return ans


2962. 统计最大元素出现至少 K 次的子数组

给你一个整数数组 nums 和一个 正整数 k

请你统计有多少满足 「 nums 中的 最大 元素」至少出现 k 次的子数组,并返回满足这一条件的子数组的数目。

子数组是数组中的一个连续元素序列。

示例 1:

输入:nums = [1,3,2,3,3], k = 2
输出:6
解释:包含元素 3 至少 2 次的子数组为:[1,3,2,3]、[1,3,2,3,3]、[3,2,3]、[3,2,3,3]、[2,3,3] 和 [3,3] 。

示例 2:

输入:nums = [1,4,2,1], k = 3
输出:0
解释:没有子数组包含元素 4 至少 3 次。

class Solution:
    def countSubarrays(self, nums: List[int], k: int) -> int:
        # 问max值至少出现k次的子数组,假设[left,right]内中max值的个数是k-1,那么nums[left-1]为max值
        max_val = max(nums)
        left = 0
        ans = 0
        count = 0  # 计算窗口内最大元素出现次数
        for right, x in enumerate(nums):
            if x == max_val:
                count += 1
            while count == k:  # 移动left直到count<k,此时left的左边就是max_val
                if nums[left] == max_val:
                    count -= 1
                left += 1
            ans += left
        return ans


2302. 统计得分小于 K 的子数组数目

一个数组的 分数 定义为数组之和 乘以 数组的长度。

  • 比方说,[1, 2, 3, 4, 5] 的分数为 (1 + 2 + 3 + 4 + 5) * 5 = 75

给你一个正整数数组 nums 和一个整数 k ,请你返回 nums 中分数 严格小于 k非空整数子数组数目

子数组 是数组中的一个连续元素序列。

示例 1:

输入:nums = [2,1,4,3,5], k = 10
输出:6
解释:
有 6 个子数组的分数小于 10 :
- [2] 分数为 2 * 1 = 2 。
- [1] 分数为 1 * 1 = 1 。
- [4] 分数为 4 * 1 = 4 。
- [3] 分数为 3 * 1 = 3 。 
- [5] 分数为 5 * 1 = 5 。
- [2,1] 分数为 (2 + 1) * 2 = 6 。
注意,子数组 [1,4] 和 [4,3,5] 不符合要求,因为它们的分数分别为 10 和 36,但我们要求子数组的分数严格小于 10 。

示例 2:

输入:nums = [1,1,1], k = 5
输出:5
解释:
除了 [1,1,1] 以外每个子数组分数都小于 5 。
[1,1,1] 分数为 (1 + 1 + 1) * 3 = 9 ,大于 5 。
所以总共有 5 个子数组得分小于 5 。

class Solution:
    def countSubarrays(self, nums: List[int], k: int) -> int:
        left = 0
        length = 0
        s = 0
        ans = 0
        for right, x in enumerate(nums):
            length += 1
            s += x
            while s * length >= k:
                s -= nums[left]
                left += 1
                length -= 1
            ans += right - left + 1
        return ans


1658. 将 x 减到 0 的最小操作数

给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。

如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1

示例 1:

输入:nums = [1,1,4,2,3], x = 5
输出:2
解释:最佳解决方案是移除后两个元素,将 x 减到 0 。

示例 2:

输入:nums = [5,6,7,8,9], x = 4
输出:-1

示例 3:

输入:nums = [3,2,20,1,1,3], x = 10
输出:5
解释:最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0 。

class Solution:
    def minOperations(self, nums: List[int], x: int) -> int:
        # 本质还是滑动窗口,求出sum(nums)-x的最长元素个数
        target = sum(nums) - x
        ans = -1
        s = 0
        left = 0
        for right, x in enumerate(nums):
            s += x
            while left <= right and s > target:
                s -= nums[left]
                left += 1
            if s == target:
                ans = max(ans, right - left + 1)
        return len(nums) - ans if ans != -1 else ans


76. 最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

示例 2:

输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。

示例 3:

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        from collections import Counter

        ct = Counter(t)  # 计算t中字符出现次数
        cs = Counter()  # 存储s中滑动窗口内字符出现次数
        left = 0
        ans_left, ans_right = -1, len(s)
        for right, x in enumerate(s):
            cs[x] += 1
            while cs >= ct:     # s中滑动窗口内的字符统计超过了t的话
                if right - left < ans_right - ans_left:
                    ans_left, ans_right = left, right
                cs[s[left]] -= 1
                left += 1
        return "" if ans_left == -1 else s[ans_left : ans_right + 1]

相关文章:

  • 通过 axios 请求回来的 HTML 字符串渲染到 Vue 界面上并添加样式
  • 五分钟快速清晰理解作用域和闭包以及封装
  • CPU 压力测试命令大全
  • 问问lua怎么写DeepSeek,,,,,
  • 基于连接池与重试机制的高效TDengine写入方案
  • IDEA 使用Maven打包时内存溢出
  • 服务器虚拟化技术深度解析:医药流通行业IT架构优化指南
  • 基于 Spring Boot 瑞吉外卖系统开发(二)
  • php调用大模型应用接口实现流式输出以及数据过滤
  • Redis常见问题排查与解决方案指南
  • HCIA二层综合实验
  • 一款轻量级的Docker日志查看器!!
  • LeetCode 每日一题 2025/3/31-2025/4/6
  • 网络攻防快速入门笔记web | 02 SQL注入
  • Elixir语言的云计算
  • 示例项目文档模板集:TaskBoard 任务管理系统
  • linux环境下的硬盘分区格式化工具介绍 fdisk,gdisk,parted,cfdisk,cgdisk,sfdisk,gparted 笔记250407
  • 构筑数字身份管理体系 赋能企业数字化转型
  • 使用阿里ECS搭建web服务器
  • GO简单开发grpc
  • 特朗普指控FBI前局长“暗示刺杀总统”,“8647”藏着什么玄机?
  • 受关税政策影响,沃尔玛将上调部分商品在美售价
  • 贝壳一季度收入增长42%:二手房市场活跃度维持在高位
  • 董军在第六届联合国维和部长级会议上作大会发言
  • 商务部:中方敦促美方尽快停止232关税措施
  • 北方产粮大省遭遇气象干旱,夏粮用水如何解决?