最大子序和
53. 最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
动态规划一
定义 dp[i] 表示以 nums[i] 结尾且包含 nums[i] 的连续子数组最大和
则 dp[i] = max(0, dp[i-1]) + nums[i]
最终的结果即遍历dp后的最大元素
首先可以在确定dp数组的每个元素时更新最终结果,不一定要完全确定了dp数组再遍历获取最终结果
其次,每次dp只跟上次的dp值有关,dp数组可以优化为一个变量
时间复杂度O(n), 空间复杂度O(1)
动态规划二
dp也可定义为当前为止连续子数组的和,如果 dp 小于 0了,说明之前的元素对后边新元素的和没有正向贡献,可以重新开始计算和,dp 置为0
func maxSubArray(nums []int) int {
if len(nums) == 0 {
return 0
}
dp, res := 0, nums[0]
for _, v := range nums {
dp = max(0, dp) + v
res = max(res, dp)
}
return res
}
func maxSubArray1(nums []int) int {
if len(nums) == 0 {
return 0
}
dp, res := 0, nums[0]
for _, v := range nums {
dp += v
res = max(res, dp)
if dp < 0 {
dp = 0
}
}
return res}
1191. K 次串联后最大子数组之和
给你一个整数数组 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
类似53的解法:
func kConcatenationMaxSum(arr []int, k int) int {
n := len(arr)
total := n * k
if total == 0 {
return 0
}
var res, dp int
for i := 0; i < total; i++ {
dp = (max(0, dp) + arr[i%n]) % 1000000007
res = max(res, dp)
}
return res
}
不过这样会超时,实际上当 k 很大的时候,没有必要遍历 nk 次,
只需要遍历 n2 次,最后的结果加上 max(0,sum) * (k-2)即可
func kConcatenationMaxSum(arr []int, k int) int {
n := len(arr)
total := min(2, k) * n
if total == 0 {
return 0
}
dp, res, sum := 0, arr[0], 0
for i := 0; i < total; i++ {
v := arr[i%n]
dp = (max(dp, 0) + v) % 1000000007
res = max(res, dp)
if i < len(arr) {
sum = (sum + v) % 1000000007
}
}
if k <= 2 {
return res
}
// 题目没有描述清楚,实际在结果为负时,需要返回 0
// return (max(sum, 0)*(k-2) + res) % 1000000007
return max(0, (max(sum, 0)*(k-2)+res)%1000000007)
}