公司里偷偷刷题记录
做一下笔记
求解子数组方式:
两种方案:
通用方案就是前缀和查找
另一种是递增序列可用的滑动窗口
有些题目如果给出来的数字有正,负。那么一定就要转化成前缀和。
如果是全正数组,可以采用前缀和+二分查找的方式。但是这个效率似乎没有滑动窗口高,不知道是不是leetcode数据量的问题,没有专门压测评估过。
习题解答
leetcode713
-
题目:https://leetcode.cn/problems/subarray-product-less-than-k/
-
乘积小于 K 的子数组
-
C++ 方案采用前缀和的形式
#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或者0if (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 + 1count += right - left + 1;}return count;
}