📅  最后修改于: 2023-12-03 15:27:36.418000             🧑  作者: Mango
给定一个整数数组和一个目标值 K,找到该数组中和最接近 K 的连续子数组。如果存在多个答案,返回任意一个即可。
示例 1:
输入: nums = [-1,2,1,-4], k = 1 输出: [0,2] 解释: 与目标值最接近的连续子数组是 [-1,2,1],其和为 2 (与 K 的差为 1),下标起始位置为 0,结束位置为 2。
这道题最容易想到的暴力解法是枚举所有子数组,然后计算它们的和是否接近 K,时间复杂度为 O(n^3)。这种暴力枚举的复杂度很高,不可取。
我们可以使用前缀和进行优化,时间复杂度可以降到 O(n^2)。具体做法是遍历数组,对于每个位置 i,记录它前面所有元素的和 preSum,然后枚举以 i 结尾的连续子数组,计算它们的和 sum,并更新离 K 最近的值即可。
代码如下:
class Solution {
public:
vector<int> subarraySumClosest(vector<int>& nums, int k) {
vector<pair<int, int>> preSum;
preSum.push_back({0, -1});
int sum = 0, n = nums.size(), diff = INT_MAX;
for (int i = 0; i < n; i++) {
sum += nums[i];
auto it = lower_bound(preSum.begin(), preSum.end(), make_pair(sum - k, 0));
for (int j = it - preSum.begin(); j < preSum.size(); j++) {
int s = sum - preSum[j].first;
if (abs(s - k) < diff) {
diff = abs(s - k);
res = {preSum[j].second + 1, i};
}
}
preSum.insert(it, {sum, i});
}
return res;
}
private:
vector<int> res{0, 0};
};
在做这道题目之前,需要了解一下前缀和的概念。前缀和就是数组前缀的和,也就是说 sum[i] 表示从 0 到 i 的元素和,它的值可以通过以下公式计算:
sum[i] = nums[0] + nums[1] + ... + nums[i]
那么,如果要计算一个子数组的和,可以用以下公式计算:
sum[i, j] = sum[j] - sum[i - 1]
其中,i 和 j 分别表示子数组的左右端点。
使用前缀和的好处是可以把子数组求和的时间复杂度降为 O(1)。
对于这道题目,我们可以先计算出数组的前缀和,然后枚举每个以 i 为右端点的连续子数组,计算它们的和,最后找到和最接近 K 的子数组即可。
具体做法如下: