[Leetcode713]乘积小于 K 的子数组

公司里偷偷刷题记录

做一下笔记

求解子数组方式:
两种方案:
通用方案就是前缀和查找
另一种是递增序列可用的滑动窗口

有些题目如果给出来的数字有正,负。那么一定就要转化成前缀和。
如果是全正数组,可以采用前缀和+二分查找的方式。但是这个效率似乎没有滑动窗口高,不知道是不是leetcode数据量的问题,没有专门压测评估过。

习题解答

leetcode713

#include <iostream>

using namespace std;
class Solution {
public:
    int numSubarrayProductLessThanK(vector<int>& nums, int k) {
        if (k == 0) {
            return 0;
        }
        int n = nums.size();
        vector<double> logPrefix(n + 1);
        for (int i = 0; i < n; i++) {
            logPrefix[i + 1] = logPrefix[i] + log(nums[i]);
            //cout << logPrefix[i] << endl;
        }

        // cout << logPrefix.end() - logPrefix.begin() << endl;
        //cout << logPrefix.end() << endl;
        double logk = log(k);
        int res = 0;
        //for (int j = 0; j < n; j++) {
        //    int mid = upper_bound(logPrefix.begin(), logPrefix.begin() + j + 1, logPrefix[j + 1] - log(k) + 1e-10) - logPrefix.begin();
        //    res += j - mid + 1;
        //}

        for (int j = 0; j < n; j++) {
            // 定义边界
            int left = 0;
            int right = j + 1;
            // 定义返回值,一般是右端,
            int mid_res = j + 1;

            // 定义特殊判断条件,若有
            double val = logPrefix[j + 1] - logk + 1e-10;

            // 二分循环            
            while (left < right) {
                // 获取中点
                int mid = (left + right) / 2;

                // 二分判断
                if (logPrefix[j+1] - logPrefix[mid] + 1e-10 < logk) {
                    // 满足条件赋值
                    mid_res = mid;
                    right = mid; // 根据实际情况向左找
                } else {
                    left = mid + 1; // 向右找。
                }
            }

            // 找到后统计,这个一行是本题的关键。
            res += j + 1 - mid_res;
        }

        return res;
    }
};
  • C语言采用滑动窗口
int numSubarrayProductLessThanK(int* nums, int numsSize, int k){
    // 乘机肯定不会小于1或者0
    if (k <= 1) {
        return 0;
    }

    int sum = 1;
    int left = 0;
    int count = 0;

    // 滑窗时候,右指针一般是快指针,用循环实现
    // 左指针一般是慢指针,通过条件判断移动
    for (int right = 0; right < numsSize; right++) {
        sum *= nums[right];
        while (sum >= k) {
            sum /= nums[left++];
        }


        // 因为都是正数所以我们可以用滑动窗口,也就是快慢指针去操作
        // 每经过一个快指针,我们可以这么统计
        // 既然从left累乘到right都满足元素严格小于k
        // 那么从left到right中的任意一个下标移动到right都可以满满足小于k
        // 那么不重复的统计子数组的数量就是从right - left + 1
        count += right - left + 1;
    }
    return count;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值