一、Problem
给你一个整数数组 arr 和一个整数 k。首先,我们要对该数组进行修改,即把原数组 arr 重复 k 次。
举个例子,如果 arr = [1, 2] 且 k = 3,那么修改后的数组就是 [1, 2, 1, 2, 1, 2]。然后,请你返回修改后的数组中的最大的子数组之和。
注意,子数组长度可以是 0,在这种情况下它的总和也是 0。由于 结果可能会很大,所以需要 模(mod) 10^9 + 7 后再返回。
输入:arr = [1,-2,1], k = 5
输出:2
提示:
1 <= arr.length <= 10^5
1 <= k <= 10^5
-10^4 <= arr[i] <= 10^4
二、Solution
方法一:最大前后缀
最暴力的方法是遍历 n × k 次,遍历时通过对数组下标进行取模防止出界,实时记录子数组最大和,但会超时。这里分类讨论一下 的情况:
- 时,直接求子数组最大和 mSum
- 时,答案可能由两个数组的最大后缀和 + 最大前缀和组成
-
时:
- 如果
(
为数组总和),那么假如数组 a 为 [x1,x2,x3],那么拼接之后变为:
[x1,x2,x3,x1,x2,x3,x1,x2,x3,x1,x2,x3],因为 sum < 0,所以 [x1,x2,x3,x1,x2,x3,x1,x2,x3
,x1,x2,x3] 加粗的这一段总和也 < 0,所以当k > 2 && sum < 0
时,考虑加入中间那一段和就不可能形成为答案了。 - 如果 ,中间那一段显然可以被考虑进来,且它的总和可以很方便计算出来: ,此时在类加上最大前后缀和即为答案。
- 如果
(
为数组总和),那么假如数组 a 为 [x1,x2,x3],那么拼接之后变为:
最后要计算的量罗列一下:最大后缀和 mSuf、最大前缀和 mPre、最大子数组和 mSum
class Solution {
public int kConcatenationMaxSum(int[] a, int k) {
int n = a.length, mod = (int) 1e9+7, mSum = a[0], mPre = a[0], mSuf = a[n-1];
int cur = 0, pre = 0, suf = 0;
for (int i = 0; i < n; i++) {
cur = cur > 0 ? (cur + a[i]) % mod : a[i]; //cur<=0的话,无论加上整数还是负数,都不会比直接用a[i]大
mSum = Math.max(mSum, cur); //最大子数组和
pre = (pre + a[i]) % mod;
suf = (suf + a[n-i-1]) % mod;
mPre = Math.max(mPre, pre); //最大前缀和
mSuf = Math.max(mSuf, suf); //最大后缀和
}
mPre = Math.max(0, mPre);
mSuf = Math.max(0, mSuf);
mSum = Math.max(0, mSum);
int tot = pre, ans = 0;
if (k == 1)
ans = mSum;
else if (k == 2)
ans = Math.max(mSum, (mPre + mSuf) % mod);
else {
if (tot <= 0) {
ans = Math.max(mSum, (mPre + mSuf) % mod);
} else {
long t = ((long) (k-2)*tot%mod + mPre + mSuf) % mod; //这里的乘法可能会导致溢出
ans = Math.max((int) t, mSum);
}
}
return ans;
}
}
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,