返回
Featured image of post 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)

滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)

https://leetcode.cn/circle/discuss/0viNMK/

滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)

定长滑动窗口

核心思路:入,更新,出

1456. 定长子串中元音的最大数目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution:
    def maxVowels(self, s: str, k: int) -> int:
        res, win = 0, 0 
        for idx, char in enumerate(s):
            """in"""
            if char in 'aeiou':
                win += 1
            if idx < k - 1:
                continue 
            """update"""
            res = max(res, win)
            """out"""
            if s[idx-(k-1)] in 'aeiou':
                win -= 1
        return res

643. 子数组最大平均数 I

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution:
    def findMaxAverage(self, nums: List[int], k: int) -> float:
        res, win = -float('inf'), 0 
        for idx, num in enumerate(nums):
            """ 入 """
            win += num 
            if idx < k - 1: continue 
            """更新"""
            res = max(res, win/k)
            """出"""
            win -= nums[idx-k+1]
        return res

1343. 大小为 K 且平均值大于等于阈值的子数组数目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution:
    def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int:
        res, win = 0, 0 
        for idx, num in enumerate(arr):
            """入"""
            win += num 
            if idx < k - 1: continue 
            """更新"""
            res += int(win >= k * threshold)
            """出"""
            win -= arr[idx-k+1]
        return res

2090. 半径为 k 的子数组平均值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution:
    def getAverages(self, nums: List[int], k: int) -> List[int]:
        res, win = [-1]*len(nums), 0
        for idx, num in enumerate(nums):
            """入"""
            win += num 
            if idx < 2 * k: continue 
            """错位更新"""
            res[idx-k] = win // (2*k+1)
            """出"""
            win -= nums[idx-2*k]
        return res

2379. 得到 K 个黑块的最少涂色次数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution:
    def minimumRecolors(self, blocks: str, k: int) -> int:
        res, win = float('inf'), 0 
        for idx, block in enumerate(blocks):
            """入"""
            win += int(block == 'W')
            if idx < k - 1: continue 
            """更新"""
            res = min(res, win)
            """出"""
            win -= int(blocks[idx-k+1] == 'W')
        return res

2841. 几乎唯一子数组的最大和

注意Counter和zip的用法,这两个让我们一方面可以快速遍历滑动窗口,一方面可以高效实现统计

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution:
    def maxSum(self, nums: List[int], m: int, k: int) -> int:
        res, win, cnt = 0, sum(nums[:k-1]), Counter(nums[:k-1])
        """
        res: 用于存储最终结果,即满足条件的子数组的最大和
        win: 当前窗口的和,初始化为前k-1个元素的和
        cnt: 当前窗口中各元素的计数器,初始化为前k-1个元素的计数
		"""
        for head, tail in zip(nums[k-1:], nums):
            """
            head: 窗口新增加的元素
            tail: 窗口将要移除的元素
            """
            win += head  # 将新元素加入窗口和
            cnt[head] += 1  # 更新新元素的计数

            if len(cnt) >= m:
                # 如果窗口内唯一元素的数量大于或等于m
                res = max(res, win)  # 更新结果为当前和与之前最大和中的较大值

            cnt[tail] -= 1  # 减少将要移除元素的计数
            win -= tail  # 从窗口和中减去将要移除的元素
            if not cnt[tail]:
                # 如果将要移除的元素在窗口中的计数为0
                del cnt[tail]  # 从计数器中删除该元素

        return res  # 返回找到的最大和

2461. 长度为 K 子数组中的最大和

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution:
    def maximumSubarraySum(self, nums: List[int], k: int) -> int:
        # 初始化结果变量,窗口和,以及计数器
        res, win, cnt = 0, sum(nums[:k-1]), Counter(nums[:k-1])
        
        # 使用滑动窗口,遍历数组
        for head, tail in zip(nums[k-1:], nums):
            # 将新元素加入窗口,并更新窗口的和与计数器
            win += head 
            cnt[head] += 1
            
            # 如果窗口中 unique 元素的数量等于 k,则更新结果
            if len(cnt) == k:
                res = max(res, win)
            
            # 移除窗口中最旧的元素,更新窗口的和与计数器
            win -= tail 
            cnt[tail] -= 1
            if cnt[tail] == 0:
                del cnt[tail]
        
        # 返回最终结果
        return res

1423. 可获得的最大点数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution:
    def maxScore(self, cardPoints: List[int], k: int) -> int:
        # 计算窗口大小,即需要保留的连续子数组的长度
        
        size = len(cardPoints) - k
        
        # 如果窗口大小为0,表示需要取走所有卡片,直接返回总和
        if not size:
            return sum(cardPoints)
        
        # 初始化最小和为无穷大,窗口和为前size-1个元素的和
        res, win = float('inf'), sum(cardPoints[:size-1])
        
        # 使用滑动窗口遍历数组
        for head, tail in zip(cardPoints[size-1:], cardPoints):
            # 将新元素加入窗口,更新窗口和
            win += head 
            # 更新最小窗口和
            res = min(res, win)
            # 移除窗口中最旧的元素,更新窗口和
            win -= tail 
        
        # 返回总和减去最小窗口和,即为最大抽取和
        return sum(cardPoints) - res

1052. 爱生气的书店老板

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution:
    def maxSatisfied(self, customers, grumpy, minutes: int) -> int:
        """
        计算在给定时间窗口内,商店最多可以满足的客户数。

        参数:
        - customers: List[int],表示每个时间点进入商店的客户数量。
        - grumpy: List[int],表示商店老板在每个时间点是否生气(1表示生气,0表示不生气)。
        - minutes: int,表示商店老板可以控制不生气的时间窗口长度。

        返回:
        - int,表示在最优控制下,商店可以满足的最多客户数。
        """

        # 计算没有控制生气情况下,自然不生气时间点的客户数总和
        base = 0
        for i, j in zip(customers, grumpy):
            base += i * (1 - j)

        # 初始化变量,用于计算通过控制不生气可以额外满足的客户数
        incr, win = 0, 0

        # 使用滑动窗口计算在不同窗口内,可以额外满足的客户数
        for i in range(len(customers)):
            win += customers[i] * grumpy[i]
            if i < minutes - 1:
                continue
            incr = max(incr, win)
            win -= customers[i - minutes + 1] * grumpy[i - minutes + 1]

        # 返回基础满意的客户数加上额外可以满意的最多客户数
        return base + incr

1652. 拆炸弹

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Solution:
    def decrypt(self, code: List[int], k: int) -> List[int]:
        """
        解密给定的代码列表。

        参数:
        - code: List[int],表示加密的代码列表。
        - k: int,表示解密的偏移量。

        返回:
        - List[int],表示解密后的代码列表。
        """

        # 初始化结果列表,长度与 code 相同,全部元素为 0
        res = [0] * len(code)
        n = len(code)

        # 将 code 列表扩展一倍,以便处理循环索引
        code.extend(code)

        if not k:
            # 如果 k 为 0,直接返回全零列表
            return res
        elif k < 0:
            # 如果 k 为负数,向左移动 |k| 个位置
            for i in range(n):
                # 计算从 i + k + n 到 i + n 的子列表的和
                res[i] = sum(code[i + k + n : i + n])
        elif k > 0:
            # 如果 k 为正数,向右移动 k 个位置
            for i in range(n):
                # 计算从 i + 1 到 i + k + 1 的子列表的和
                res[i] = sum(code[i + 1 : i + k + 1])

        return res

3439. 重新安排会议得到最多空余时间 I

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution:
    def maxFreeTime(self, eventTime: int, k: int, startTime: List[int], endTime: List[int]) -> int:
        """
        计算在给定事件时间内,最大的空闲时间长度。

        参数:
        - eventTime: int,总事件时间长度。
        - k: int,表示窗口大小或限制。
        - startTime: List[int],表示各事件的开始时间列表。
        - endTime: List[int],表示各事件的结束时间列表。

        返回:
        - int,表示最大的空闲时间长度。
        """

        # 计算事件之间的空闲时间间隔
        gap, n = [], len(startTime)
        for i in range(n + 1):
            if not i:
                # 第一个事件开始前的空闲时间
                gap.append(startTime[0])
            elif i == n:
                # 最后一个事件结束后到总时间结束的空闲时间
                gap.append(eventTime - endTime[i - 1])
            else:
                # 相邻事件之间的空闲时间
                gap.append(startTime[i] - endTime[i - 1])

        # 初始化窗口总和和结果变量
        win, res = 0, 0

        # 使用滑动窗口计算最大空闲时间
        for i in range(len(gap)):
            win += gap[i]
            if i < k:
                continue
            res = max(res, win)
            win -= gap[i - k]

        return res

2134. 最少交换次数来组合所有的 1 II

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution:
    def minSwaps(self, nums: List[int]) -> int:
        """
        计算将所有1聚在一起所需的最小交换次数。

        参数:
        - nums: List[int],一个包含0和1的整数列表。

        返回:
        - int,表示将所有1聚在一起所需的最小交换次数。
        """

        n, k = len(nums), sum(nums)
        if not k:
            return 0  # 如果没有1,不需要任何交换

        # 将 nums 扩展一倍,处理环形情况
        nums.extend(nums)

        # 初始化窗口内1的个数和结果变量
        win, res = 0, 0

        # 使用滑动窗口计算窗口内1的最大个数
        for i in range(2 * n):
            win += nums[i]
            if i < k - 1:
                continue  # 窗口尚未形成,继续添加
            res = max(res, win)  # 更新窗口内1的最大个数
            win -= nums[i - k + 1]  # 移除窗口最左边的元素

        # 最小交换次数等于窗口大小 k 减去窗口内1的最大个数 res
        return k - res

1297. 子串的最大出现次数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from collections import Counter

class Solution:
    def maxFreq(self, s: str, maxLetters: int, minSize: int, maxSize: int) -> int:
        """
        这个函数用于找到字符串s中满足特定条件的子字符串的最高出现频率。
        
        参数:
        - s: 目标字符串,类型为str
        - maxLetters: 子字符串中允许的最大不同字符数,类型为int
        - minSize: 子字符串的最小长度,类型为int
        - maxSize: 子字符串的最大长度,类型为int
        
        返回:
        - 满足条件的子字符串的最大出现次数,类型为int
        """
        
        # 使用两个计数器:cnt1跟踪当前窗口内每个字符的出现次数,cnt2跟踪满足条件的子字符串的出现次数
        cnt1, cnt2 = Counter(), Counter()
        res = 0  # 用来记录最高出现次数的结果变量
        
        # 遍历字符串s,i为当前窗口的结束索引
        for i in range(len(s)):
            cnt1[s[i]] += 1  # 将当前字符加入cnt1
            
            # 如果窗口大小还没达到minSize,跳过后面的检查
            if i < minSize - 1:
                continue
            
            # 计算窗口的起始索引
            j = i - minSize + 1
            
            # 当窗口大小达到minSize时,开始检查条件
            if len(cnt1) <= maxLetters:
                # 提取子字符串
                sub = s[j:i+1]
                # 更新cnt2,记录该子字符串的出现次数
                cnt2[sub] += 1
                # 更新结果变量,取当前结果和cnt2[sub]的最大值
                res = max(res, cnt2[sub])
            
            # 移动窗口起始位置,移除起始处的字符,并更新cnt1
            cnt1[s[j]] -= 1
            if cnt1[s[j]] == 0:
                del cnt1[s[j]]
        
        # 遍历完成后,返回最高出现次数
        return res

不定长滑动窗口

不定长滑动窗口主要分为三类:求最长子数组,求最短子数组,以及求子数组个数。

求最长/最大子数组

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        """
        计算字符串 s 中最长的无重复字符子串的长度。

        参数:
        s (str): 输入的字符串

        返回:
        int: 最长无重复字符子串的长度
        """
        # 使用 defaultdict 来记录字符出现的频率
        cnt = defaultdict(int)
        # res 记录最大不重复子串的长度,l 是滑动窗口的左边界
        res, l = 0, 0
        # 遍历字符串 s,r 是滑动窗口的右边界
        for r, c in enumerate(s):
            """
            对于每一个右边界 r,检查字符 s[r] 是否已经在 cnt 中:
            - 如果已经在 cnt 中,说明窗口内有重复字符,需要循环移动左边界 l
            - 如果不在 cnt 中,将字符加入 cnt,并更新最大长度 res
            """
            # 如果当前字符 s[r] 已经在 cnt 中,说明窗口内有重复字符
            while cnt[s[r]]:
                # 移除左边界的字符,并更新 cnt 和 l
                cnt[s[l]] -= 1
                # 如果某个字符的计数变为0,从 cnt 中删除该字符
                if cnt[s[l]] == 0:
                    del cnt[s[l]]
                l += 1
            # 当前字符 s[r] 不在 cnt 中,加入 cnt
            cnt[s[r]] += 1
            # 更新最大长度 res
            res = max(res, r - l + 1)
        return res

3090. 每个字符最多出现两次的最长子字符串

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from typing import List
from collections import defaultdict

class Solution:
    def maximumLengthSubstring(self, s: str) -> int:
        """
        计算字符串 s 中最长的子串长度,该子串中每个字符最多出现两次。

        参数:
        s (str): 输入的字符串

        返回:
        int: 符合条件的最长子串长度
        """
        # 使用 defaultdict 来记录字符出现的频率
        cnt = defaultdict(int)
        # res 记录满足条件的最长子串长度,l 是滑动窗口的左边界
        res, l = 0, 0
        # 遍历字符串 s,r 是滑动窗口的右边界
        for r, c in enumerate(s):
            """
            对于每一个右边界 r,检查字符 s[r] 在 cnt 中的频率:
            - 如果字符 s[r] 的频率已经达到2,需要移动左边界 l,直到 s[r] 的频率小于2
            - 如果字符 s[r] 的频率小于2,将其加入 cnt,并更新最大长度 res。
            """
            # 如果当前字符 s[r] 的频率已经等于2,需要移动左边界
            while cnt[s[r]] == 2:
                # 减少左边界的字符频率,如果频率为0则从 cnt 中移除
                cnt[s[l]] -= 1
                if cnt[s[l]] == 0:
                    del cnt[s[l]]
                l += 1
            # 增加当前字符 s[r] 的频率
            cnt[s[r]] += 1
            # 更新最大长度 res
            res = max(res, r - l + 1)
        return res

1493. 删掉一个元素以后全为 1 的最长子数组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from typing import List

class Solution:
    def longestSubarray(self, nums: List[int]) -> int:
        """
        计算数组 nums 中最长的子数组长度,该子数组可以包含至多一个零。

        参数:
        nums (List[int]): 输入的整数数组,只包含0和1。

        返回:
        int: 符合条件的最长子数组长度
        """
        # res 记录最大子数组长度,zero 记录当前窗口内零的个数,l 是滑动窗口的左边界
        res, zero, l = 0, 0, 0
        # 遍历数组 nums,r 是滑动窗口的右边界
        for r, num in enumerate(nums):
            """
            对于每一个右边界 r,检查 num 是否为零:
            - 如果 zero 已经为1且 num 为零,需要移动左边界 l,直到窗口内零的个数小于1。
            - 如果 zero 小于1,将 num 加入窗口,并更新最大长度 res。
            """
            # 如果 zero 已经为1且 num 为零,移动左边界直到窗口内零的个数小于1
            while zero == 1 and not num:
                # 如果左边界的数为零,zero 减1
                if not nums[l]:
                    zero -= 1
                l += 1
            # 如果 num 为零,zero 加1
            zero += int(not num)
            # 更新最大长度 res
            res = max(res, r - l + 1)
        # 由于题目要求至少删除一个元素,所以结果减1
        return res - 1

1208. 尽可能使字符串相等

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from typing import List

class Solution:
    def equalSubstring(self, s: str, t: str, maxCost: int) -> int:
        """
        计算两个字符串 s 和 t 的最长公共子串长度,
        其中可以进行字符转换,每次转换的代价是两个字符的 ASCII 码差值,
        总代价不能超过 maxCost。

        参数:
        s (str): 第一个字符串
        t (str): 第二个字符串
        maxCost (int): 最大允许的转换总代价

        返回:
        int: 满足条件的最长公共子串长度
        """
        # cost 记录当前窗口的总转换代价,res 记录最大子串长度,l 是滑动窗口的左边界
        cost, res, l = 0, 0, 0
        # 遍历字符串 s 和 t,r 是滑动窗口的右边界
        for r, (cs, ct) in enumerate(zip(s, t)):
            """
            对于每一组字符对 (cs, ct):
            - 计算转换代价 diff = |ord(cs) - ord(ct)|
            - 将 diff 加到当前窗口的总代价 cost 上
            - 如果 cost 超过 maxCost,需要移动左边界 l,减少成本
            - 更新最大子串长度 res
            """
            # 计算当前字符转换的代价
            diff = abs(ord(cs) - ord(ct))
            cost += diff
            # 如果总代价超过 maxCost,移动左边界
            while cost > maxCost:
                # 减去左边界的转换代价
                cost -= abs(ord(s[l]) - ord(t[l]))
                l += 1
            # 更新最大子串长度
            res = max(res, r - l + 1)
        return res

04. 水果成篮

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution:
    def totalFruit(self, fruits: List[int]) -> int:
        # 初始化计数器、结果变量和左指针
        cnt = Counter()  # 用于统计窗口内每种水果的数量
        res = 0  # 用于记录最大子数组的长度
        l = 0  # 滑动窗口的左边界,初始为0
        
        # 遍历数组,r是当前右边界索引,f是当前的水果类型
        for r, f in enumerate(fruits):
            # 如果窗口内已经有两种不同的水果,并且当前水果不在窗口内,
            # 则需要移动左边界,直到窗口内少于两种不同的水果
            while len(cnt) == 2 and not cnt[f]:
                # 减少左边界水果的计数
                cnt[fruits[l]] -= 1
                # 如果某水果的计数为0,从计数器中移除
                if cnt[fruits[l]] == 0:
                    del cnt[fruits[l]]
                # 左边界向右移动
                l += 1
            # 将当前水果加入窗口,并增加其计数
            cnt[f] += 1
            # 更新结果为当前窗口大小和之前最大值中的较大者
            res = max(res, r - l + 1)
        
        # 返回最大窗口大小
        return res

1695. 删除子数组的最大得分

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution:
    def maximumUniqueSubarray(self, nums: List[int]) -> int:
        # 初始化计数器、结果变量和左指针
        cnt = Counter()  # 用于统计窗口内每个数字的出现次数
        res = 0  # 用于记录最大子数组和
        l = 0  # 滑动窗口的左边界,初始为0
        current_sum = 0  # 用于记录当前窗口的和,避免每次计算sum
        
        for r, num in enumerate(nums):
            # 如果当前数字已经在窗口中(cnt[num] > 0),则需要移动左边界
            while cnt[num]:
                # 从计数器中移除左边界的数字,并更新当前和
                cnt[nums[l]] -= 1
                current_sum -= nums[l]
                # 如果该数字的计数为0,从计数器中删除
                if cnt[nums[l]] == 0:
                    del cnt[nums[l]]
                l += 1  # 左边界右移
            # 将当前数字加入窗口,更新计数器和当前和
            cnt[num] += 1
            current_sum += num
            # 更新结果为当前窗口和与之前最大值中的较大者
            res = max(res, current_sum)
        
        return res

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution:
    def maxSubarrayLength(self, nums: List[int], k: int) -> int:
        # 初始化计数器、结果变量和左指针
        cnt = Counter()  # 用于统计窗口内每个数字的出现次数
        res = 0  # 用于记录最大子数组长度
        l = 0  # 滑动窗口的左边界,初始为0
        
        for r, num in enumerate(nums):
            # 如果当前数字在窗口中出现的次数达到k次,则需要移动左边界
            while cnt[num] == k:
                # 减少左边界数字的计数
                cnt[nums[l]] -= 1
                # 如果某数字的计数为0,从计数器中移除
                if cnt[nums[l]] == 0:
                    del cnt[nums[l]]
                # 左边界右移
                l += 1
            # 将当前数字加入窗口,增加其计数
            cnt[num] += 1
            # 更新结果为当前窗口大小和之前最大值中的较大者
            res = max(res, r - l + 1)
        
        # 返回最大窗口大小
        return res

2024. 考试的最大困扰度

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Solution:
    def maxConsecutiveAnswers(self, answerKey: str, k: int) -> int:
        cnt = Counter()  # 初始化计数器
        res = 0  # 初始化结果变量
        l = 0  # 初始化左指针
        for r, c in enumerate(answerKey):
            cnt[c] += 1
            while min(cnt['T'], cnt['F']) > k:
                # 如果窗口内'T'和'F'的数量都超过k,则需要移动左边界
                cnt[answerKey[l]] -= 1
                l += 1
            res = max(res, r - l + 1)
        return res

1004. 最大连续1的个数 III

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution:
    def longestOnes(self, nums: List[int], k: int) -> int:
        # 初始化变量:
        # zero: 记录当前窗口内0的个数
        # res: 记录最大窗口大小
        # l: 滑动窗口的左边界,初始为0
        zero, res, l = 0, 0, 0
        
        # 遍历数组,r为滑动窗口的右边界
        for r in range(len(nums)):
            # 如果nums[r]是0,zero增加1
            zero += int(not nums[r])
            
            # 如果当前窗口内0的个数超过k,需要移动左边界l
            while zero > k:
                # 如果nums[l]是0,zero减少1
                zero -= int(not nums[l])
                # 左边界向右移动一位
                l += 1
            
            # 更新结果,取当前窗口大小和之前的最大值
            res = max(res, r - l + 1)
        
        # 返回最大窗口大小
        return res

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from typing import List

class Solution:
    def minOperations(self, nums: List[int], x: int) -> int:
        """
        计算最少操作次数,以使数组两端移除的元素之和等于x。
        
        参数:
        nums (List[int]): 整数数组。
        x (int): 目标和。
        
        返回:
        int: 最小操作次数,如果无法实现则返回-1。
        """
        # 计算目标和,即数组总和减去x
        target = sum(nums) - x
        # 如果目标和小于0,表示数组总和小于x,无法实现,返回-1
        if target < 0:
            return -1
        # 如果目标和等于0,表示不需要移除任何元素,直接返回数组长度
        if target == 0:
            return len(nums)
        
        # 初始化变量:
        # res: 记录满足条件的窗口的最大长度
        # win: 记录当前窗口的和
        # l: 窗口的左边界,初始为0
        # found: 标记是否找到满足条件的窗口
        res, win, l, found = 0, 0, 0, False
        
        # 遍历数组,r为窗口的右边界
        for r in range(len(nums)):
            # 将nums[r]加入窗口和
            win += nums[r]
            # 如果窗口和超过目标和,需要移动左边界
            while win > target and l < r:
                # 从窗口和中减去nums[l]
                win -= nums[l]
                # 左边界向右移动一位
                l += 1
            # 如果窗口和等于目标和,更新最大窗口长度
            if win == target:
                res = max(res, r - l + 1)
                found = True
        
        # 如果找到满足条件的窗口,返回数组长度减去最大窗口长度,否则返回-1
        return len(nums) - res if found else -1

Licensed under CC BY-NC-SA 4.0
© 2023 - 2025 壹壹贰捌· 0Days
共书写了265.7k字·共 93篇文章 京ICP备2023035941号-1