LeetcodeDay15 不定长滑动窗口

模版

l = 0
  for r in range(n):
  	count[r] += 1

	while (不满足):
		count[l] -= 1
		l += 1

3 无重复的最长连续子串

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        count = {}
        res = 0
        n = len(s)
        l = 0
        for r in range(n):
            c = s[r]
            count[c] = count.get(c, 0) + 1
            while count[c] > 1:
                count[s[l]] -= 1
                l += 1
            res = max(res, r - l + 1)
        return res

76 最小覆盖子串

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

注意:

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

示例 1:

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

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        ans_left, ans_right = -1, len(s)
        left = 0
        cnt_s = Counter()  # s 子串字母的出现次数
        cnt_t = Counter(t)  # t 中字母的出现次数
        for right, c in enumerate(s):  # 移动子串右端点
            cnt_s[c] += 1  # 右端点字母移入子串
            while cnt_s >= cnt_t:  # 涵盖
                if right - left < ans_right - ans_left:  # 找到更短的子串
                    ans_left, ans_right = left, right  # 记录此时的左右端点
                cnt_s[s[left]] -= 1  # 左端点字母移出子串
                left += 1  # 移动子串左端点
        return "" if ans_left < 0 else s[ans_left: ans_right + 1]

注意counter的用法, 可直接用>=; 同时咱们对答案的更新应该在while内部

209 最小长度的连续子数组s.t. 之和大于target

(略)
给定一个含有 n 个正整数的数组和一个正整数 target 。

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

904 只能拿两种水果

class Solution:
    def totalFruit(self, fruits: List[int]) -> int:
        count = {}
        l = 0
        ans = 0
        for r in range(len(fruits)):
            count[fruits[r]] = count.get(fruits[r], 0) + 1
            while (len(count)) > 2:
                count[fruits[l]] -= 1
                if count[fruits[l]] == 0:
                    count.pop(fruits[l])
                l += 1
            ans = max(ans, r - l + 1)
        return ans

992. K 个不同整数的子数组

给定一个正整数数组 nums和一个整数 k,返回 nums 中 「好子数组」 的数目。

如果 nums 的某个子数组中不同整数的个数恰好为 k,则称 nums 的这个连续、不一定不同的子数组为 「好子数组 」。

输入:nums = [1,2,1,2,3], k = 2
输出:7
解释:恰好由 2 个不同整数组成的子数组:[1,2], [2,1], [1,2], [2,3], [1,2,1], [2,1,2], [1,2,1,2].

一开始我的做法还是:

class Solution:
    def subarraysWithKDistinct(self, nums: List[int], k: int) -> int:
        count = {}
        l = 0 
        ans = 0

        for r in range(len(nums)):
            count[nums[r]] = count.get(nums[r], 0) + 1
            while len(count) > k:
                count[nums[l]] -= 1
                if count[nums[l]] == 0:
                    count.pop(nums[l])
                l += 1     
            ans += r - l + 1
        return ans

其实这只解决最多k个的情况, 不是恰好k, 那我们直接用最多k
减去最多k-1不就可以了

class Solution:
    def subarraysWithKDistinct(self, nums: List[int], k: int) -> int:
        def helper(k):
            ans = 0
            n = len(nums)
            l = 0
            g = defaultdict(int)
            for r, x in enumerate(nums):
                g[x] += 1
                while len(g) > k:
                    g[nums[l]] -= 1
                    if g[nums[l]] == 0:
                        del g[nums[l]]
                    l += 1
                # 从上面循环出来一定保证g.keys() == k
                ans += r - l + 1
            return ans
        return helper(k) - helper(k-1)      

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

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

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

灵神一句话, 正难则反, 直接点通任督二脉, 求两边 = x<=> 中间=sum - x, 则转化为求最长子数组s.t.和为sum - x

class Solution:
    def minOperations(self, nums: List[int], x: int) -> int:
        target = sum(nums) - x
        if target < 0: return -1  # 全部移除也无法满足要求
        ans = -1
        left = s = 0
        for right, x in enumerate(nums):
            s += x
            while s > target:  # 缩小子数组长度
                s -= nums[left]
                left += 1
            if s == target:
                ans = max(ans, right - left + 1)
        return -1 if ans < 0 else len(nums) - ans

1004 最大连续1的个数 III

给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k 个 0 ,则返回 数组中连续 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。

一句话, 窗口内最多k个0

class Solution:
    def longestOnes(self, nums: List[int], k: int) -> int:
        # 转换为滑动窗口, 窗口内最多k个0
        count = 0
        l = 0
        n = len(nums)
        ans = 0
        for r in range(len(nums)):
            if nums[r] == 0:
                count += 1
            while count > k:
                if nums[l] == 0:
                    count -= 1
                l += 1
            ans = max(ans, r - l + 1)
        return ans

1248 统计恰好k个奇数的数组数量

给你一个整数数组 nums 和一个整数 k。如果某个连续子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。

请返回这个数组中 「优美子数组」 的数目。

输入:nums = [1,1,2,1,1], k = 3
输出:2
解释:包含 3 个奇数的子数组是 [1,1,2,1] 和 [1,2,1,1] 。

class Solution:
    def numberOfSubarrays(self, nums: List[int], k: int) -> int:
        def help(k):
            l = 0
            n = len(nums)
            ans = 0
            count = 0  # 计数奇数的个数,而不是使用字典
            for r in range(n):
                if nums[r] % 2 == 1:
                    count += 1
                while count > k:
                    if nums[l] % 2 == 1:
                        count -= 1
                    l += 1
                ans += r - l + 1
            return ans
        return help(k) - help(k-1)

你可能感兴趣的:(leetcode,算法,python,数据结构)