Loading

10 前缀和+哈希:和为K的子数组 560

浅浅试一下

image

我的思考:大眼一看,这道题应该是 不定长滑动窗口。
那我只要先从头找到一个最接近k或者等于k的leftsum,然后慢慢向右滑动即可。
OK,开始写代码

一个小时过去了,
没写出来。

唉,看题解吧。

区域和检索-数组不可变 303

一个长度为n的数组,有多少个子数组呢?
对于任意一个子数组

  • 子数组的起点有n个选择。
  • 子数组的终点有n-起点个选择
    如长度为3的数组[1, 2, 3]
  • 子数组起点有3个选择
  • 起点为0的子数组终点也有\(3-0=3\)个选择

综上,一个数组的子数组数量级是\(O(n^{2})\)

同样地,一个数组的前缀和是\(O(n)\)

任意子数组的和都可以表示为两个前缀和的差值。

再做最初的那道题

点击查看代码
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int ans = 0;
        //计算前缀和
        vector<int> vec(nums.size() + 1);
        vec[0] = 0;
        for (int i = 0; i < nums.size(); ++i) {
            vec[i+1] = vec[i] + nums[i];
        }
        unordered_map<int, int> m;
        for (auto& val: vec) {
            if (m.contains(val - k))
                ans += m[val - k];
            ++m[val];
        }
        return ans; 
    }
};

其实这个代码,我不是很能理解。
思路是这样的:

  • 先计算前缀和
  • 然后因为每个子数组都可以表示成两个前缀和的差值,所以只需要遍历前缀和数组,看哈希表中是否有相应的值即可。

注:vec[0]表示第0个元素前缀和为0

时间复杂度:O(n)
空间复杂度:O(n)

问:为什么这题不适合用滑动窗口做?

答:滑动窗口需要满足单调性,当右端点元素进入窗口时,窗口元素和是不能减少的。本题 nums 包含负数,当负数进入窗口时,窗口左端点反而要向左移动,导致算法复杂度不是线性的。

额,这道题好难理解,头大。

变形题

    1. 改成计算元素和等于 k 的最短子数组,要怎么做?
    1. 改成计算元素和等于 k 的最长子数组,要怎么做?
    1. 改成计算元素和等于 k 的所有子数组的长度之和,要怎么做?
    1. 改成元素和至多为 k,要怎么做?见 363. 矩形区域不超过 K 的最大数值和。

今天就到这里吧,学废了。
变形题想不明白,标记一下吧。

2025-05-27 10:06:49 星期二
书接上回,咱们再接再厉!

长度最小的子数组 209

点击查看代码
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        //不定长滑动窗口
        int leftSum = 0;
        int left = 0;
        int minLen = INT_MAX;
        for (int i = 0; i < nums.size(); ++i) {
            leftSum += nums[i];
            if (leftSum < target)
                continue;
            while (leftSum >= target) {
                minLen = min(minLen, i - left + 1);
                leftSum -= nums[left];
                ++left;
            }
        }
        if (minLen == INT_MAX)
            return 0;
        return minLen;
    }
};

受到昨天前缀和+哈希的影响,我感觉这道题也是这个套路,然后写了写没什么思路。
我不知道二次遍历那块应该咋修改,遂放弃。
然后我一看这个数组都是正数,复合滑动窗口的单调性,所以双指针滑动,就做出来了。
时间复杂度:O(n)
空间复杂度:O(1)

听灵神这道题的讲解。

首先考虑暴力做法,找出所有的子数组。
依次枚举每个左端点,向右扩展n + (n-1) + (n-2) +... + 1,\(O(n^{2})\)

优化成O(n),参考我的做法。

还可以继续优化成\(O(\log n)\)

有时间再做吧。

713 乘积小于K的子数组

点击查看代码
class Solution {
public:
    int numSubarrayProductLessThanK(vector<int>& nums, int k) {
        int tmp = 1;
        int ans = 0;
        int left = 0, right = 0;
        for (int right = 0; right < nums.size(); ++right) {
            tmp *= nums[right]; //l r
            //[l r] [l+1 r] [r r]
            while (left <= right && tmp >= k) { 
                tmp = tmp / nums[left];
                ++left;
            }
            if (tmp < k)
                ans = ans + right - left + 1;
          }
        return ans;    
    }
};

天哪,吐了!
虽然根上一题是一样的套路,但是总感觉哪里不太一样。
呼,今天就到这里吧。

posted @ 2025-05-26 11:46  王仲康  阅读(39)  评论(0)    收藏  举报