一、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;
}
}
複雜度分析
- 時間複雜度:,
- 空間複雜度:,