给你一个整数数组 arr
和一个整数 k
。
首先,我们要对该数组进行修改,即把原数组 arr
重复 k
次。
举个例子,如果
arr = [1, 2]
且k = 3
,那么修改后的数组就是[1, 2, 1, 2, 1, 2]
。
然后,请你返回修改后的数组中的最大的子数组之和。
注意,子数组长度可以是 0
,在这种情况下它的总和也是 0
。
由于 结果可能会很大,所以需要 模(mod) 10^9 + 7
后再返回。
示例 1:
输入:arr = [1,2], k = 3
输出:9
示例 2:
输入:arr = [1,-2,1], k = 5
输出:2
示例 3:
输入:arr = [-1,-2], k = 7
输出:0
提示:
1 <= arr.length <= 10^5
1 <= k <= 10^5
-10^4 <= arr[i] <= 10^4
思路:
直接把 k 个 arr 连在一起然后找答案显然不现实……
先把简单的情况讨论一下,
1. 如果arr里所有数字都比0小,那么很简单,一个数字都不应该选,直接返回0
2. 如果arr里所有数字都比0大,那么也很简单,直接把所有数字都选上,返回 sum(arr) * k
从2推广一下,如果sum(arr) > 0, 那么如何处理?
现在有 k 个 arr, 已知sum(arr) > 0, 那么是不是每个arr全都选上就可以取到最大值了呢?
分析一下可知,对于中间的(k - 2)的arr来说,每一个arr都应该全部选中,因为只要多选一个arr, 总和就一定会更大,
但是对于第一个arr, 和最后一个arr则不同,
第一个arr可能是这样的,[-5, 6, 7, 2], 此时我们需要判断从哪一个数开始,能得到最大的局部子数组和,因此可以利用后缀和数组产生[10, 15, 9, 2]这样的中间结果,第 i 位 代表从末尾到 arr[i] 之间的数字之和,中间数组的最大值就是最大局部子数组和,
最后一个arr则是同理,利用前缀和找最大局部子数组和。
对于sum(arr) <= 0 的这种情况,我们只需要在 arr + arr 的范围里用普通的dp算法找最大子数组和即可,
这是因为已知 sum(arr)非正数,那么每多加一个arr,只会让目前的数组和更小。
因此只需考虑arr + arr的情况,在普通的数组或循环数组里找一次即可。
class Solution(object):
def kConcatenationMaxSum(self, arr, k):
"""
:type arr: List[int]
:type k: int
:rtype: int
"""
if max(arr) < 0:
return 0
if sum(arr) > 0:
res = sum(arr) * (k - 2)
dp = [0] * len(arr)
dp[0] = arr[0]
for i in range(1,len(arr)):
dp[i] = dp[i - 1] + arr[i]
res += max(dp)
dp = [0] * len(arr)
dp[-1] = arr[-1]
for i in range(len(arr) - 2, -1, -1):
dp[i] = dp[i + 1] + arr[i]
res += max(dp)
return (res)% (10 ** 9 + 7)
arr = arr + arr
dp = [0] * len(arr)
dp[0] = arr[0]
for i in range(1,len(arr)):
if dp[i - 1] > 0:
dp[i] = dp[i - 1] + arr[i]
else:
dp[i] = arr[i]
return max(dp) % (10 ** 9 + 7)