[M前缀和] lc100384. 长度为 K 的子数组的能量值 I、II(前缀和+二分+双周赛137_1+双周赛137_2)

1. 题目来源

链接:100384. 长度为 K 的子数组的能量值 II

2. 题目解析

本来好好的一个前缀和问题,被我硬生生搞成二分,最近研究了下整数二分的题目,本想实践一下,就花了较多实践。


前缀和:

  • 前缀和不仅可以求取区间中的数字总和。也可以求取区间中的一些特性的总和。
  • 声明前缀和数组 f,其中 f[i] 表示下标在 i 位置的上升数组的最大长度。
  • 如果 nums[i]-nums[i-1]==1 那么即认为 i 位置是上升数组,则在前缀和数组中对 f[i] ++ ; 否则仅让 f[i]=f[i-1] 即可。
  • 那么 f[i]-f[j] 就相当于 [i, j] 中是否存在 k-1 个值,因为 f[j] 这个起始值是为 0 的。

二分:

  • 统计每一个满足区间的左边界、右边界。
  • 针对每一个 i 在左边界数组中二分查找最后一个小于 i+k-1 这个右区间边界的位置。
  • 此时,若能找到的话,则说明左区间边界已经有一个比待查区间小的值了。
  • 只需判断这个区间是否将待查区间完全包含即可。
  • 为了方便处理,左右添加哨兵节点。
  • 且由于二分做法的特殊性,如果 k 或者 n 等于 1 的话,可以直接返回原数组即可。

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

class Solution {
public:
    vector<int> resultsArray(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> f(n);
        for (int i = 1; i < n; i ++ ) f[i] = f[i - 1] + (nums[i] - nums[i - 1] == 1);

        vector<int> res;
        for (int i = 0; i < n - k + 1; i ++ ) {
            int j = i + k - 1;
            if (f[j] - f[i] == k - 1) res.push_back(nums[j]);
            else res.push_back(-1);
        }

        return res;
    }
};

二分:

class Solution {
public:
    vector<int> resultsArray(vector<int>& nums, int k) {
        int n = nums.size();
        if (k == 1 || n == 1) return nums;      // 二分做法的特殊性

        vector<int> f;
        vector<int> l, r;
        l.push_back(-1), r.push_back(-1);

        for (int i = 0, j = 1; i < n; i ++ ) {
            j = i + 1;
            bool flag = false;
            while (j < n && nums[j] - nums[j - 1] == 1) {
                flag = true;
                j ++ ;
            }
            if (flag) l.push_back(i), r.push_back(j - 1);
            i = j - 1;
        }

        l.push_back(1e9), r.push_back(1e9);

        vector<int> res;
        for (int i = 0; i < n - k + 1; i ++ ) {
            int j = i + k - 1;
            int t = lower_bound(l.begin(), l.end(), j) - l.begin() - 1;
            if (t < 0 || l[t] == 1e9 || l[t] == -1 || r[t] < j || l[t] > i) {
                res.push_back(-1);
                continue;
            }

            res.push_back(nums[j]);
        }

        return res;
    }
};
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值