【LeetCode】209. 长度最小的子数组 Minimum Size Subarray Sum(C++)


题目来源:https://leetcode-cn.com/problems/minimum-size-subarray-sum/

题目描述

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

示例 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


进阶:

如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(nlog(n)) 时间复杂度的解法。

题目大意

  • 找一个不含有重复字符的最长字串,使用一个记录出现次数的cnt数组,通过滑动窗口+双指针的形式即可

滑动窗口+双指针

  • 维护一个可变长的窗口,不断地移动左右指针,取最长的窗口长度
class Solution {
    
    
public:
    int minSubArrayLen(int target, vector<int>& nums) {
    
    
        int len = nums.size(), left = 0, right = 0;
        int ans = INT_MAX, cnt = 0;
        bool flag = false;
        for(; right < len ; ++right){
    
    
            cnt += nums[right];
            while(cnt >= target){
    
    
                ans = min(ans, right - left + 1);
                cnt -= nums[left];
                ++left;
                flag = true;
            }
        }
        if(!flag)   return 0;
        return ans;
    }
};

复杂度分析

  • 时间复杂度:O(n)。左右指针均移动了n次
  • 空间复杂度:O(1)

前缀和+二分查找

  • 虽说滑动窗口复杂度是O(n),进阶题目却要nlogn,别问,问就是O(nlogn)有可能优于O(n),想到logn可以联想到在有序数组的条件下,进行二分查找元素的时间复杂度为O(logn),让数组有序的方法,一种是直接进行排序,一种是通过前缀和构造单调序列,题目中的元素值必定为正数,所以前缀和一定是单调序列
  • 其中sum[i]表示从nums[0]到nums[i - 1]的元素和。得到前缀和之后,对于每个开始下标i,可通过二分查找sum[i, bound]从i到bound的累加和(也即是i到bound的连续子数组的累加和),如果找到满足sum[i,bound]>=target,更新子数组的最短长度(此时子数组的长度为bound - i + 1)
  • 此处可以直接调用stl封装好的lower_bound对vector直接进行二分查找计算(面试的时候八成不给直接调用),此处手动实现返回第一个大于等于target的元素对应的下标值index
class Solution {
    
    
public:
    int minSubArrayLen(int target, vector<int>& nums) {
    
    
        int len = nums.size(), ans = INT_MAX;
        int sums[len + 1];
        sums[0] = nums[0];
        // sums[0] = 0 意味着前 0 个元素的前缀和为 0
        // sums[1] = A[0] 前 1 个元素的前缀和为 A[0]
        for(int i = 1 ; i <= len ; ++i)  sums[i] = sums[i - 1] + nums[i - 1];
        for(int i = 1 ; i <= len ; ++i){
    
    
            int newTarget = target + sums[i - 1];
            int bound = BinarySearch(newTarget, sums, 0, len);
            if(bound != -1){
    
    
               ans = min(ans, bound - i + 1);
            }
        }
        return ans == INT_MAX ? 0 : ans;
    }
    int BinarySearch(int target, int* nums, int left, int right){
    
    
        while(left <= right){
    
    
            int mid = right + ((left - right) >> 1);
            if(nums[mid] < target)  left = mid + 1;
            else{
    
    
                if(mid == 1 || nums[mid - 1] < target)  return mid;
                else    right = mid - 1;
            }
        }
        return -1;
    }
};

复杂度分析

  • 时间复杂度:O(nlogn)。n为数组的长度,n次循环 * logn为二分查找的平均复杂度
  • 空间复杂度:O(n)。n为数组的长度,维护一个前缀和数组

猜你喜欢

转载自blog.csdn.net/lr_shadow/article/details/113947699
今日推荐