题目描述
思路
思路参考leetcode官方题解:
要计算三个无重叠子数组的最大和,我们可以枚举第三个子数组的位置,同时维护前两个无重叠子数组的最大和及其位置。
要计算两个无重叠子数组的最大和,我们可以枚举第二个子数组的位置,同时维护第一个子数组的最大和及其位置。
因此,我们首先来解决单个子数组的最大和问题,然后解决两个无重叠子数组的最大和问题,最后解决三个无重叠子数组的最大和问题。
前言一:单个子数组的最大和
我们用滑动窗口来解决这一问题。
滑动窗口是数组/字符串问题中常用的抽象概念。窗口通常是指在数组/字符串中由开始和结束索引定义的一系列元素的集合,即闭区间 [i,j]
。而滑动窗口是指可以将两个边界向某一方向「滑动」的窗口。例如,我们将 [i,j]
向右滑动 1
个元素,它将变为 [i+1,j+1]
。
设 sum1
为大小为 k
的窗口的元素和,当窗口从[i−k+1,i]
向右滑动 1
个元素后,sum1
增加了nums[i+1]
,减少了nums[i-k+1]
。据此我们可以 O(1)
地计算出向右滑动 1
个元素后的窗口的元素和。
我们从[0,k−1]
开始,不断地向右滑动窗口,直至窗口右端点到达数组末尾时停止。统计这一过程中的sum1
的最大值(记作maxsum1
)及其对应位置。
前言二:两个无重叠子数组的最大和
我们使用两个大小为k
的滑动窗口。设sum1
为第一个滑动窗口的元素和,该滑动窗口从[0,k-1]
开始;sum2
为第二个滑动窗口的元素和,该滑动窗口从[k,2k-1]
开始。
我们同时向右滑动这两个窗口,并维护sum1
的最大值maxsum1
及其对应位置。每次滑动时,计算当前maxsum1
与sum2
之和。统计这一过程中的maxsum1+sum2
的最大值(记作maxsum12
)及其对应位置。
滑动窗口法
回到本题,我们使用三个大小为k
的滑动窗口。设sum1
为第一个滑动窗口的元素和,该滑动窗口从[0,k-1]
;sum2
为第二个滑动窗口的元素和,该滑动窗口从[k,2k-1]
;sum3
为第三个滑动窗口的元素和,该滑动窗口从[2k,3k-1]
开始。
我们同时向右滑动这三个窗口,按照前言二的方法并维护maxsum12
及其对应位置。每次滑动前,计算当时maxsum12
与sum3
之和。统计这一过程中的maxsum12+sum3
的最大值及其对应位置。
对于题目要求的最小字典序,由于我们是从左到右遍历的,并且当且仅当元素和超过最大元素和时才更新,从而保证求出来的下标列表时字典序最小的。
代码实现
Leetcode代码实现
class Solution {
public:
vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k)
{
int sum1=0,maxsum1=0,maxsum1Index=0;
int sum2=0,maxsum12=0,maxsum12Index1=0,maxsum12Index2=0;
int sum3=0,maxtotal=0;
vector<int> ans;
for(int i=2*k;i<nums.size();i++)
{
sum1+=nums[i-2*k];
sum2+=nums[i-k];
sum3+=nums[i];
if(i>=3*k-1)
{
if(sum1>maxsum1)
{
maxsum1=sum1;
maxsum1Index=i-3*k+1;
}
if(maxsum1+sum2>maxsum12)
{
maxsum12=maxsum1+sum2;
maxsum12Index1=maxsum1Index;
maxsum12Index2=i-2*k+1;
}
if(maxsum12+sum3>maxtotal)
{
maxtotal=maxsum12+sum3;
ans={
maxsum12Index1,maxsum12Index2,i-k+1};
}
sum1-=nums[i-3*k+1];
sum2-=nums[i-2*k+1];
sum3-=nums[i-k+1];
}
}
return ans;
}
};
完整代码实现(vs2019运行)
#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k)
{
int sum1 = 0, maxsum1 = 0, maxsum1Index = 0;
int sum2 = 0, maxsum12 = 0, maxsum12Index1 = 0, maxsum12Index2 = 0;
int sum3 = 0, maxtotal = 0;
vector<int> ans;
for (int i = 2 * k; i < nums.size(); i++)
{
sum1 += nums[i - 2 * k];
sum2 += nums[i - k];
sum3 += nums[i];
if (i >= 3 * k - 1)
{
if (sum1 > maxsum1)
{
maxsum1 = sum1;
maxsum1Index = i - 3 * k + 1;
}
if (maxsum1 + sum2 > maxsum12)
{
maxsum12 = maxsum1 + sum2;
maxsum12Index1 = maxsum1Index;
maxsum12Index2 = i - 2 * k + 1;
}
if (maxsum12 + sum3 > maxtotal)
{
maxtotal = maxsum12 + sum3;
ans = {
maxsum12Index1,maxsum12Index2,i - k + 1 };
}
sum1 -= nums[i - 3 * k + 1];
sum2 -= nums[i - 2 * k + 1];
sum3 -= nums[i - k + 1];
}
}
return ans;
}
};
int main()
{
vector<int> nums ={
1,3,2,6,5,3,7,8,9,2,0 };
Solution s;
vector<int> res;
res=s.maxSumOfThreeSubarrays(nums, 2);
for (auto p : res)
{
cout << p << " ";
}
}
总结
本题最大的收获是对滑动窗口的更深一步了解,不仅仅局限于一个滑动窗口求最大值,更明白了多个滑动窗口联动求最大和的过程。
在代码实现中有三个if判断,依次确定了sum1的最大,在sum1最大时 sum1+sum2的最大,在sum1+sum2最大时 sum1+sum2+sum3的最大。
值得注意的一点是:虽然三个窗口是联动执行的,但是不意味着三个窗口一定是永远相邻的,因为要通过max值的比较来决定下标是否更改。