Medium

题目描述

给定一个整数数组 arr 和一个整数 k,通过重复数组 k 次来修改数组。

例如,如果 arr = [1, 2]k = 3,那么修改后的数组将是 [1, 2, 1, 2, 1, 2]

返回修改后数组中的最大子数组和。注意子数组的长度可以是 0,在这种情况下它的和为 0。

由于答案可能很大,请返回答案对 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

解题思路

这道题需要分情况讨论来解决:

核心思路:

  1. k=1 的情况:直接使用 Kadane 算法求最大子数组和
  2. k>1 的情况:需要考虑三种可能的最大子数组位置:
    • 完全在某一个数组副本内(与k=1相同)
    • 跨越数组边界,包含前缀和后缀
    • 包含完整的中间数组副本

具体分析:

  • 先计算原数组的总和 totalSum、最大前缀和 maxPrefix、最大后缀和 maxSuffix
  • 使用 Kadane 算法计算单个数组的最大子数组和 maxKadane
  • 对于 k=1,答案就是 max(0, maxKadane)
  • 对于 k>1:
    • 如果 totalSum > 0,最优解可能是 maxSuffix + maxPrefix + (k-2) * totalSum
    • 否则最优解是跨越两个数组的子数组,即在两个连续副本中用 Kadane 算法
  • 取所有情况的最大值

时间优化: 当 k>1 时,我们只需要考虑两个连续的数组副本,因为如果包含更多完整副本且 totalSum ≤ 0,答案不会更优。

代码实现

class Solution {
public:
    int kConcatenationMaxSum(vector<int>& arr, int k) {
        const int MOD = 1e9 + 7;
        int n = arr.size();
        
        // Calculate total sum
        long long totalSum = 0;
        for (int x : arr) {
            totalSum += x;
        }
        
        // Kadane's algorithm for single array
        long long maxKadane = 0, currentSum = 0;
        for (int x : arr) {
            currentSum = max((long long)x, currentSum + x);
            maxKadane = max(maxKadane, currentSum);
        }
        
        if (k == 1) {
            return maxKadane % MOD;
        }
        
        // Calculate max prefix sum
        long long maxPrefix = 0, prefixSum = 0;
        for (int x : arr) {
            prefixSum += x;
            maxPrefix = max(maxPrefix, prefixSum);
        }
        
        // Calculate max suffix sum
        long long maxSuffix = 0, suffixSum = 0;
        for (int i = n - 1; i >= 0; i--) {
            suffixSum += arr[i];
            maxSuffix = max(maxSuffix, suffixSum);
        }
        
        // Kadane on two concatenated arrays
        long long maxTwo = 0;
        currentSum = 0;
        for (int i = 0; i < 2 * n; i++) {
            currentSum = max((long long)arr[i % n], currentSum + arr[i % n]);
            maxTwo = max(maxTwo, currentSum);
        }
        
        long long result = maxTwo;
        if (totalSum > 0) {
            result = max(result, maxSuffix + maxPrefix + (k - 2) * totalSum);
        }
        
        return result % MOD;
    }
};
class Solution:
    def kConcatenationMaxSum(self, arr: List[int], k: int) -> int:
        MOD = 10**9 + 7
        n = len(arr)
        
        # Calculate total sum
        total_sum = sum(arr)
        
        # Kadane's algorithm for single array
        max_kadane = current_sum = 0
        for x in arr:
            current_sum = max(x, current_sum + x)
            max_kadane = max(max_kadane, current_sum)
        
        if k == 1:
            return max_kadane % MOD
        
        # Calculate max prefix sum
        max_prefix = prefix_sum = 0
        for x in arr:
            prefix_sum += x
            max_prefix = max(max_prefix, prefix_sum)
        
        # Calculate max suffix sum
        max_suffix = suffix_sum = 0
        for x in reversed(arr):
            suffix_sum += x
            max_suffix = max(max_suffix, suffix_sum)
        
        # Kadane on two concatenated arrays
        max_two = current_sum = 0
        for i in range(2 * n):
            current_sum = max(arr[i % n], current_sum + arr[i % n])
            max_two = max(max_two, current_sum)
        
        result = max_two
        if total_sum > 0:
            result = max(result, max_suffix + max_prefix + (k - 2) * total_sum)
        
        return result % MOD
public class Solution {
    public int KConcatenationMaxSum(int[] arr, int k) {
        const int MOD = 1000000007;
        int n = arr.Length;
        
        // Calculate total sum
        long totalSum = arr.Sum(x => (long)x);
        
        // Kadane's algorithm for single array
        long maxKadane = 0, currentSum = 0;
        foreach (int x in arr) {
            currentSum = Math.Max(x, currentSum + x);
            maxKadane = Math.Max(maxKadane, currentSum);
        }
        
        if (k == 1) {
            return (int)(maxKadane % MOD);
        }
        
        // Calculate max prefix sum
        long maxPrefix = 0, prefixSum = 0;
        foreach (int x in arr) {
            prefixSum += x;
            maxPrefix = Math.Max(maxPrefix, prefixSum);
        }
        
        // Calculate max suffix sum
        long maxSuffix = 0, suffixSum = 0;
        for (int i = n - 1; i >= 0; i--) {
            suffixSum += arr[i];
            maxSuffix = Math.Max(maxSuffix, suffixSum);
        }
        
        // Kadane on two concatenated arrays
        long maxTwo = 0;
        currentSum = 0;
        for (int i = 0; i < 2 * n; i++) {
            currentSum = Math.Max(arr[i % n], currentSum + arr[i % n]);
            maxTwo = Math.Max(maxTwo, currentSum);
        }
        
        long result = maxTwo;
        if (totalSum > 0) {
            result = Math.Max(result, maxSuffix + maxPrefix + (k - 2) * totalSum);
        }
        
        return (int)(result % MOD);
    }
}
var kConcatenationMaxSum = function(arr, k) {
    const MOD = 1000000007;
    const n = arr.length;
    
    function kadane(nums) {
        let maxSum = 0;
        let currentSum = 0;
        for (let num of nums) {
            currentSum = Math.max(0, currentSum + num);
            maxSum = Math.max(maxSum, currentSum);
        }
        return maxSum;
    }
    
    if (k === 1) {
        return kadane(arr) % MOD;
    }
    
    const totalSum = arr.reduce((sum, num) => sum + num, 0);
    const doubleArr = [...arr, ...arr];
    const maxInDouble = kadane(doubleArr);
    
    if (totalSum <= 0) {
        return maxInDouble % MOD;
    }
    
    const maxPrefix = kadane(arr.slice().reverse()).valueOf();
    let prefixSum = 0;
    let maxPrefixSum = 0;
    for (let num of arr) {
        prefixSum += num;
        maxPrefixSum = Math.max(maxPrefixSum, prefixSum);
    }
    
    let suffixSum = 0;
    let maxSuffixSum = 0;
    for (let i = n - 1; i >= 0; i--) {
        suffixSum += arr[i];
        maxSuffixSum = Math.max(maxSuffixSum, suffixSum);
    }
    
    const result = Math.max(maxInDouble, (maxPrefixSum + maxSuffixSum + (k - 2) * totalSum) % MOD);
    return result % MOD;
};

复杂度分析

复杂度类型分析
时间复杂度O(n),其中 n 是数组长度。虽然有多次遍历,但都是常数次,总体仍为线性时间
空间复杂度O(1),只使用了常数额外空间存储各种和值