无重叠双指定长度连续子数组最大和问题:高效解法咨询
嘿,这个问题我当初面试的时候也碰到过,暴力枚举所有组合确实在大数据集下复杂度太高(O(n²)),完全扛不住。其实我们可以用前缀和+预处理最大区间和的思路,把时间复杂度降到O(n),空间复杂度可以做到O(n)甚至O(1),下面给你详细拆解:
核心思路
问题本质是找两个不重叠的连续子数组(长度分别为K和J),使得它们的和最大。我们可以把问题拆成两种互斥的情况,分别计算最大值后取较大值:
- Karen的K长度子数组在John的J长度子数组左边(两段不重叠)
- John的J长度子数组在Karen的K长度子数组左边(两段不重叠)
步骤1:预处理前缀和数组
首先构建前缀和数组pre_sum,用来快速计算任意连续子数组的和:
pre_sum[0] = 0pre_sum[i] = arr[0] + arr[1] + ... + arr[i-1](也就是前i个元素的和)
这样,任意一段从索引a到b(闭区间,0-based)的子数组和可以用pre_sum[b+1] - pre_sum[a]快速算出,时间复杂度O(1)。
步骤2:计算两种情况的最大值
情况1:K在左,J在右
我们需要:
预处理
max_k数组:max_k[i]表示从数组开头到第i个位置(包含i),所有长度为K的子数组的最大和。- 遍历数组,从i=K-1开始(因为长度为K的子数组最早结束在K-1位置):
max_k[i] = max(max_k[i-1], pre_sum[i+1] - pre_sum[i+1-K]) - 简单说,就是每次比较“之前的最大K区间和”和“以当前位置结尾的K区间和”,取较大值。
- 遍历数组,从i=K-1开始(因为长度为K的子数组最早结束在K-1位置):
预处理
max_j_from数组:max_j_from[i]表示从第i个位置开始到数组末尾,所有长度为J的子数组的最大和。- 从右往左遍历,从i=n-J开始(因为长度为J的子数组最早开始在n-J位置,结束在n-1):
max_j_from[i] = max(max_j_from[i+1], pre_sum[i+J] - pre_sum[i]) - 也就是每次比较“之后的最大J区间和”和“以当前位置开头的J区间和”,取较大值。
- 从右往左遍历,从i=n-J开始(因为长度为J的子数组最早开始在n-J位置,结束在n-1):
遍历所有合法的分割点:分割点i需要满足
i >= K-1(K区间至少有K个元素)且i+1 <= n-J(J区间从i+1开始至少有J个元素),计算max_k[i] + max_j_from[i+1],取这些值的最大值就是情况1的结果。
情况2:J在左,K在右
和情况1逻辑完全对称:
- 预处理
max_j数组:max_j[i]表示从数组开头到第i个位置(包含i),所有长度为J的子数组的最大和。 - 预处理
max_k_from数组:max_k_from[i]表示从第i个位置开始到数组末尾,所有长度为K的子数组的最大和。 - 遍历合法分割点i(
i >= J-1且i+1 <= n-K),计算max_j[i] + max_k_from[i+1],取最大值作为情况2的结果。
步骤3:取两种情况的最大值
最终答案就是情况1和情况2的最大值中的较大者。
示例验证
用你给出的例子:arr = [4,5,7,8,3,1],K=3,J=2:
- 前缀和数组
pre_sum = [0,4,9,16,24,27,28] - 情况1计算得最大值27,情况2计算得最大值27,最终结果就是27,和示例一致。
空间优化(可选)
如果想把空间复杂度降到O(1),其实不需要存储完整的max_k、max_j_from等数组,只用变量记录当前的最大值即可:
- 计算
max_k的时候,用一个变量current_max_k,每次更新为max(current_max_k, 当前K区间和) - 计算
max_j_from的时候,用一个变量current_max_j,从右往左遍历更新 - 同理其他数组也可以用变量代替,这样除了前缀和数组(甚至前缀和也可以边遍历边计算,不用存储),不需要额外的数组空间。
内容的提问来源于stack exchange,提问作者Simon Lombard

