题目
思路:首先是长度为k的子数组的和。这个好计算。题目要求返回的是三个和最大的子数组的第一个数字的下标。下标要尽可能小。如果只要求这样,题目就很简单了。还有个要求是各个子数组不重叠。要想不重叠首先得要求下标不重叠。子数组1下标是:0,1,2;子数组2下标如果是1,2,3那肯定会重叠。其次要计算出每个子数组的最大值,最小值。在找下个子数组的时候,范围不能在这之间。我应该是傻眼了。需要考虑的因素比较多。看别人怎么解吧。
学习:看了别人的解法,明白题目意思了解错了。不重叠的子数组就是指下标不重叠,而不是各个值不重叠。要返回的三个下标(idx1,idx2,idx3) 满足idx2>=idx1+kidx2>=idx1+k 并且 idx3>=idx2+kidx3>=idx2+k。
还要学习的一点是:当计算了长度为k的子数组的和,计算之后其实得到了一个sums的数组。sums[i]表示以i为起点的长度为k的子数组的和。sums的长度是n-k+1。如此看待问题会比较简单一些。
对于求这三个下标我一开始的做法是:
int max1 = sums[0],max2 = sums[0+k],max3 = sums[0+2*k];int idx1 = 0,idx2 = k,idx3 = 2*k;for (i = 1; i < k; i++) {if (sums[i] > max1) {max1 = sums[i];idx1 = i;}if (i+k <n && sums[i+k] > max2) {max2 = sums[i+k];idx2 = i+k;}if (i+2*k < n && sums[i+2*k] > max3) {max3 = sums[i+2*k];idx3 = i+2*k;}}
这样做的问题是:可能idx2没有变化,idx1变化了。这样他们的差值就不是k,就不能符合条件了。所以需要固定idx2的位置。n=sums.length;那么idx1∈[0,idx2−k]idx1∈[0,idx2−k] 并且 idx3∈[idx2+1,n−1]idx3∈[idx2+1,n−1]。而idx2能够取值的范围是[k,n−k−1][k,n−k−1]。(n是原始数组长度)。
当idx2位置固定,对于idx1应该是[0,idx2-k]那个最大数的下标。这个问题可以用动态规划解决。用一个数组先保存从[0,当前位置]值最大的下标。动态转移方程式: 如果sums[i]>sums[left[i−1]]sums[i]>sums[left[i−1]],则left[i]=ileft[i]=i;否则left[i]=left[i−1]left[i]=left[i−1]。当然这里不用动态规划,那就每次循环一下从0到idx2-k找到最大值的下标。
右边也是一样。当idx2位置固定,对于idx3应该是[idx2+1,n-1]那个最大数的下标。用动态规划解决。用一个数组先保存从[当前位置,n-1]值最大的下标。动态转移方程式: 如果sums[i]>=sums[right[i+1]]sums[i]>=sums[right[i+1]],则right[i]=iright[i]=i;否则right[i]=right[i+1]right[i]=right[i+1]。这里的条件是>=>=是因为,结果要求返回下标最小值。
学习链接
public int[] maxSumOfThreeSubarrays(int[] nums, int k) {int n = nums.length;//计算和,因为入参都是正数,所以不需要初始化sums,否则可以都初始化为Integer.MIN_VALUE;int[] sums = new int[n];//这步可以优化//0,1 1,2 2,3 3,4 4,5 5,6int sum = 0;for (int i = 0; i <n; i++) {sum += nums[i];if(i>=k) sum -= nums[i-k];if(i>=k-1) sums[i-k+1] = sum; }//与左边的数组比较int[] left = new int[n-k+1];int best = 0;for(int i=0;i<left.length;i++){if(sums[i]>sums[best]) best = i;left[i] = best;}//跟右边的数组比较int[] right = new int[n-k+1];best = right.length - 1;for(int i = right.length -1;i>=0;i--){if(sums[i]>=sums[best]) best = i;right[i] = best;}int idx1 = -1,idx2 = -1,idx3 = -1;for (int j = k; j < right.length-k; j++) {int l = left[j-k],r = right[j+k];if(idx1==-1 || (sums[idx1]+sums[idx2]+sums[idx3] < sums[l]+sums[j]+sums[r])){idx1 = l;idx2 = j;idx3 = r;}}return new int[]{idx1,idx2,idx3};}
复杂度分析:时间复杂度O(n),空间复杂度O(n)。