1. 问题背景与核心挑战
今天咱们来啃一道LeetCode上的硬骨头——第689题"三个无重叠子数组的最大和"。这道题在2023年字节跳动秋招中出现了变种,也是谷歌面试的高频题目。题目要求我们从一个整数数组nums中,找到三个长度为k的非重叠子数组,使得这三个子数组的和最大,并返回这三个子数组的起始索引。
我第一次看到这个题目时,直觉告诉我这肯定要用滑动窗口,但具体怎么处理三个窗口的约束条件,确实让我琢磨了好一阵子。经过多次调试和优化,终于总结出了一套清晰的解题思路,下面就把我的完整思考过程和实现方案分享给大家。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 暴力解法与初步优化
2.1 最直观的暴力解法
最直接的想法就是枚举所有可能的三元组组合。假设数组长度为n,我们需要:
- 枚举第一个子数组的起始位置i(0 ≤ i ≤ n-3k)
- 枚举第二个子数组的起始位置j(i+k ≤ j ≤ n-2k)
- 枚举第三个子数组的起始位置l(j+k ≤ l ≤ n-k)
- 计算这三个子数组的和,记录最大值
这种解法的时间复杂度是O(n³),当n=10000时(LeetCode的测试用例规模),这显然会超时。
2.2 前缀和优化
我们可以先用前缀和数组预处理,将子数组求和的时间复杂度从O(k)降到O(1)。前缀和数组prefixSum的构建方式如下:
java复制int[] prefixSum = new int[nums.length + 1];
for (int i = 0; i < nums.length; i++) {
prefixSum[i+1] = prefixSum[i] + nums[i];
}
这样,任意子数组nums[i..j]的和就可以用prefixSum[j+1] - prefixSum[i]来计算。优化后,暴力解法的时间复杂度仍然是O(n³),但实际运行时间会有所改善。
3. 动态规划解法
3.1 动态规划状态定义
这道题的正解是动态规划。我们需要定义三个DP数组:
- dp1[i]:表示nums[0..i]范围内,1个长度为k的子数组的最大和
- dp2[i]:表示nums[0..i]范围内,2个长度为k的子数组的最大和
- dp3[i]:表示nums[0..i]范围内,3个长度为k的子数组
