环己三烯的冬眠舱

天天网抑云,偶尔读点书。

0%

每日Hard - Day 2

Tags

日期 2022年9月13日
是否独立完成
涉及算法 滑动窗口(或前缀和)、动态规划

题面

LeetCode 689. 三个无重叠子数组的最大和

给你一个整数数组 nums 和一个整数 k ,找出三个长度为 k 、互不重叠、且全部数字和(3 * k 项)最大的子数组,并返回这三个子数组。

以下标的数组形式返回结果,数组中的每一项分别指示每个子数组的起始位置(下标从 0 开始)。如果有多个结果,返回字典序最小的一个。

示例 1:

1
2
3
4
输入:nums = [1,2,1,2,6,7,5,1], k = 2
输出:[0,3,5]
解释:子数组 [1, 2], [2, 6], [7, 5] 对应的起始下标为 [0, 3, 5]。
也可以取 [2, 1], 但是结果 [1, 3, 5] 在字典序上更大。

示例 2:

1
2
输入:nums = [1,2,1,2,1,2,1,2,1], k = 2
输出:[0,2,4]

题解

滑动窗口 + 动态规划

看到多个子数组求和第一反应就是前缀和,后来看题解发现滑动窗口也可以,而且更省内存一点。

求完所有长度为k的子数组和后,可以维护一个left和一个right数组,其中left 存从左开始到当前位置最大的和,right存从右开始到当前位置最大的和。我们枚举处于中间位置的数组,再通过leftright快速检索与这个位置不冲突的左侧最大子数组和以及右侧最大子数组和,在枚举过程中不断更新结果就可以了。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
public:
vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) {
vector<int> sum;
int cur = 0;
for (int i = 0; i < k; i++) cur += nums[i];
sum.push_back(cur);
int n = nums.size();
for (int i = k; i < n; i++) {
cur = cur + nums[i] - nums[i - k];
sum.push_back(cur);
}
n = sum.size();
vector<int> left(n, 0);
vector<int> right(n, n - 1);
for (int i = 1; i < n; i++) {
if (sum[i] > sum[left[i - 1]]) left[i] = i;
else left[i] = left[i - 1];
}
for (int i = n - 2; i >= 0; i--) {
if (sum[i] >= sum[right[i + 1]]) right[i] = i;
else right[i] = right[i + 1];
}
int mx = 0;
vector<int> res(3, 0);
for (int i = k; i < n - k; i++) {
if (mx < sum[i] + sum[left[i - k]] + sum[right[i + k]]) {
mx = sum[i] + sum[left[i - k]] + sum[right[i + k]];
res = { left[i - k],i,right[i + k] };
}
}
return res;
}
};

有的没的

没事听点歌(One Republic - Counting Stars)