LeetCode 209-长度最小的字符串

60 阅读2分钟

题目

给定一个含有 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

提示:

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

思路

解法一:滑动窗口(自)

滑动窗口经典题型,left、right指针初始化0,然后外框架right指针一直移动<n,内部第一个满足sum >= target的情况,然后一直移动left指针,计算r-l+1,取最小值更新答案,直到sum < target,进入下一个right。

时间复杂度O(N),空间复杂度O(1)

代码一: 滑动窗口

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        # 双指针
        left = 0
        right = 0
        n = len(nums)
        res = n + 1
        s = 0 # 记录滑动窗口的和
        while right < n:
            s += nums[right]
            if s < target:
                right += 1
                continue
            
            while s >= target:
                res = min(res, right-left+1)
                s -= nums[left]
                left += 1

            right += 1
        return res if res != n + 1 else 0

解法2: 前缀和+二分查找定位

解法2也就是官解2,利用题意的所有值都大于0,可以发现数组的区间和一定是单调递增的,可以通过构造前缀和sums数组,sums数组长度是 len(nums)+1,其中 sums[i] = Sum(nums[0],...nums[i-1])。

如何找到target?

通过在sums中查找target,利用Sum(nums[i],...,nums[j]) = sums(j-1) - sums(i) >= target即可。

  • 遍历sums,从1开始,往右查找sum(j),使其满足 sums(j) - sums(i) >= target,由于sums是单调递增的,所以从i位置开始可以使用二分查找定位到满足条件的sums的j下标
  • 在找到满足条件的最左j坐标后,更新结果,res = min(res, j-i+1)

时间复杂度O(Nlog(N)),空间复杂度O(N)

代码二:前缀和+二分定位

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        # 二分查找
        def get_lowest_bound(arr, l, r, target):
            while l < r:
                mid = (l + r) >> 1
                if arr[mid] < target:
                    l = mid + 1
                else:
                    r = mid
            return l if arr[l] >= target else -1
        

        # 前缀和+二分查找
        n = len(nums)
        res = n + 1

        # 构造前缀和列表
        sums = [0]
        for i in nums:
            sums.append(sums[-1] + i)
        
        for i in range(1, n+1):
            bound = get_lowest_bound(sums, i, n, sums[i-1]+target)
            if bound != -1:
                res = min(res, bound- i+1)
        return res if res != n + 1 else 0