公司里偷偷刷题记录
做一下笔记
求解子数组方式:
两种方案:
通用方案就是前缀和查找
另一种是递增序列可用的滑动窗口
有些题目如果给出来的数字有正,负。那么一定就要转化成前缀和。
如果是全正数组,可以采用前缀和+二分查找的方式。但是这个效率似乎没有滑动窗口高,不知道是不是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或者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;
}