力扣689-三个无重叠子数组的最大和

  |  

摘要: 同时预处理前缀和和后缀和

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings


本文看一个前缀和的问题。需要同时维护前缀信息和后缀信息。


$1 题目

给定数组 nums 由正整数组成,找到三个互不重叠的子数组的最大和。

每个子数组的长度为k,我们要使这3*k个项的和最大化。

返回每个区间起始索引的列表(索引从 0 开始)。如果有多个结果,返回字典序最小的一个。

1
2
3
4
注意:
nums.length的范围在[1, 20000]之间。
nums[i]的范围在[1, 65535]之间。
k的范围在[1, floor(nums.length / 3)]之间。

示例:
输入: [1,2,1,2,6,7,5,1], 2
输出: [0, 3, 5]
解释: 子数组 [1, 2], [2, 6], [7, 5] 对应的起始索引为 [0, 3, 5]。
我们也可以取 [2, 1], 但是结果 [1, 3, 5] 在字典序上更大。


$2 题解

算法: 前缀和

给定窗口长度 k 后,nums 上的窗口总共有 nw = n - k + 1 个。定义 w[i] := 第 i 个窗口的和。

然后我们在 w 的基础上,定义:

  • left_max[i] := 第 [0..i] 个窗口中,和最大的窗口的左端点
  • right_max[i] := 第 [i..n-1] 个窗口中,和最大的窗口的左端点

然后我们枚举中间窗口的左端点 j = [k, nw - k - 1],该窗口的和为 w[j],然后我们查询 left_maxright_max 得到左边窗口和右边窗口最大的和。其中:

  • 左边的和最大且不重合的窗口的左端点为 left_max[j - k],对应的最大和为 w[left_max[j - k]]
  • 右边的和最大且不重合的窗口的左端点为 right_max[j + k],对应的最大和为 w[right_max[j + k]]

枚举 j 的过程中更新答案。

代码(C++)

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
35
36
37
38
39
40
41
42
43
44
45
46
47
class Solution {
public:
vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) {
int n = nums.size();
// 窗口有 n - k + 1 个
int nw = n - k + 1;
vector<int> w(nw, 0); // w[i] := 第 i 个窗口的和
int w_sum = 0;
for(int i = 0; i < k; ++i)
w_sum += nums[i];
w[0] = w_sum;
for(int i = k; i < n; ++i)
{
w_sum -= nums[i - k];
w_sum += nums[i];
w[i - k + 1] = w_sum;
}
vector<int> left_max(nw), right_max(nw);
left_max[0] = 0, right_max[nw - 1] = nw - 1;
for(int i = 1; i < nw; ++i)
{
left_max[i] = left_max[i - 1];
if(w[i] > w[left_max[i - 1]])
left_max[i] = i;
}
for(int i = nw - 2; i >= 0; --i)
{
right_max[i] = right_max[i + 1];
if(w[i] >= w[right_max[i + 1]])
right_max[i] = i;
}
int max_sum = 0;
vector<int> result(3);
for(int j = k; j <= nw - k - 1; ++j)
{
int sum = w[left_max[j - k]] + w[j] + w[right_max[j + k]];
if(sum > max_sum)
{
max_sum = sum;
result[0] = left_max[j - k];
result[1] = j;
result[2] = right_max[j + k];
}
}
return result;
}
};

Share