leetcode-hot100-子串

560. 和为 K 的子数组

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数

子数组是数组中元素的连续非空序列。

示例 1:

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

示例 2:

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

双层循环,i,j表示子数组的左、右边界,遍历所有的子串,判断其和是否等于k。O(N x N)

class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        cnt, n =  0, len(nums)
        for i in range(n):
            sum = 0
            for j in range(i, n):
                sum += nums[j]
                if (sum == k): cnt += 1
        return cnt

另一种思路是前缀和,差分法可以求任意子序列的和。
前缀和类似于数据积分,prefix[i]表示子数组0~i的和,prefix[i] - prefix[j]表示j+1, i之间的和。通常prefix初始化时长度为n+1,此时prefix[0]表示空数组的长度,即一个元素也没有;这样prefix[1] - prefix[0]才是只有一个元素时的前缀和。

class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
      cnt, n =  0, len(nums)
      pre = [0] * (n + 1) # 初始化
      for i in range(1, n + 1):
          pre[i] = pre[i - 1] + nums[i - 1]
      for i in range(1, n + 1):
          for j in range(i, n + 1):
              if (pre[j] - pre[i - 1] == k): cnt += 1
      return cnt

记录个数,方便复用,同时减少不必要的循环。

class Solution:
    def subarraySum(self, nums, k):
        result = 0
        sum = 0
        prefix = {0: 1}

        for n in nums:
            sum += n
            if (sum - k) in prefix:
                result += prefix[sum - k]
            prefix[sum] = prefix.get(sum, 0) + 1

        return result

239. 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:
[3,3,5,5,6,7]

固定窗口,循环遍历,找到所有符合长度条件的子数组,确定子数组内的最大值,保存在结果数组中。

另一种方法,采用优先队列/大顶堆,遍历元素,维护长度=k的结构,保存在数组中。

import heapq

heapq默认是小顶堆,通过对元素取负,转换成小顶堆,最后翻转得到目标值。

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        # 注意 Python 默认的优先队列是小根堆
        q = [(-nums[i], i) for i in range(k)]
        heapq.heapify(q)

        ans = [-q[0][0]]
        for i in range(k, n):
            heapq.heappush(q, (-nums[i], i))
            while q[0][1] <= i - k:
                heapq.heappop(q)
            ans.append(-q[0][0])
        
        return ans

76. 最小覆盖子串

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

注意:

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

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

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        if not t or not s:
            return ""
        
        # 字典t_dict记录字符串t中每个字符的数量
        t_dict = {}
        for char in t:
            t_dict[char] = t_dict.get(char, 0) + 1
        
        # 需要找到的字符数量
        required = len(t_dict)
        
        # 左右指针初始化
        l, r = 0, 0
        
        # formed用于记录当前窗口中满足t_dict字符数量的字符种类数量
        formed = 0
        
        # 字典window_counts记录当前窗口中每个字符的数量
        window_counts = {}
        
        # (窗口长度, 左指针, 右指针)
        ans = float("inf"), None, None
        
        while r < len(s):
            # 扩大窗口
            character = s[r]
            window_counts[character] = window_counts.get(character, 0) + 1
            
            # 如果当前字符的数量满足了t_dict中的要求,则formed增加
            if character in t_dict and window_counts[character] == t_dict[character]:
                formed += 1
            
            # 当formed等于required,说明当前窗口已经覆盖了t中的所有字符
            while l <= r and formed == required:
                character = s[l]
                
                # 更新答案
                if r - l + 1 < ans[0]:
                    ans = (r - l + 1, l, r)
                
                # 缩小窗口
                window_counts[character] -= 1
                if character in t_dict and window_counts[character] < t_dict[character]:
                    formed -= 1
                l += 1
            
            # 移动右指针
            r += 1
        
        return "" if ans[0] == float("inf") else s[ans[1]:(ans[2] + 1)]

我们使用两个指针 lr 来表示滑动窗口的左右边界。通过移动这两个指针,我们探索所有可能的窗口以找到最小的覆盖子串。我们还使用了两个字典 t_dictwindow_counts 来分别跟踪 t 中的字符及其数量以及当前窗口中的字符及其数量。通过比较这两个字典,我们可以知道当前窗口是否包含了 t 中的所有字符。变量 formed 用于跟踪当前窗口中满足 t 字符数量要求的字符种类数。当 formed 等于 required 时,我们知道当前窗口是一个有效的覆盖子串,并尝试通过移动左指针来缩小它的大小。我们不断更新最小覆盖子串的长度和位置,直到找到答案或遍历完整个字符串 s

  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值