力扣1031题指南:如何高效寻找两个不重叠子数组的最大和?
一、问题理解与解题思路
力扣1031题要求我们在一个整数数组中找到两个不重叠的、长度固定的子数组,使它们的和最大。这道题考察了动态规划和滑动窗口的综合应用能力。
关键点分析
不重叠条件:两个子数组不能有任何重叠的元素
固定长度:两个子数组的长度分别为firstLen和secondLen
最大和:在所有可能的组合中,找出和最大的那一对
解题思路
前缀和预处理:计算前缀和数组,方便快速计算任意子数组的和
滑动窗口应用:使用滑动窗口技术计算所有可能子数组的和
动态规划思想:记录从左到右和从右到左的子数组最大值
组合比较:尝试两种可能的排列顺序(firstLen在前或secondLen在前)
二、代码实现详解
前缀和计算
前缀和数组prefixSum
的构建是解决子数组和问题的经典方法。通过prefixSum[i+1] - prefixSum[i+1-len]
可以快速计算长度为len的子数组和。
四个辅助数组
firstMax
:记录从左到右到当前位置为止,firstLen长度的子数组最大和secondMax
:记录从左到右到当前位置为止,secondLen长度的子数组最大和firstMaxFromRight
:记录从右到左从当前位置开始,firstLen长度的子数组最大和secondMaxFromRight
:记录从右到左从当前位置开始,secondLen长度的子数组最大和
两种排列情况
firstLen在前:遍历所有可能的firstLen子数组,然后在其右侧找最大的secondLen子数组
secondLen在前:遍历所有可能的secondLen子数组,然后在其右侧找最大的firstLen子数组
三、完整代码
class Solution { public: int maxSumTwoNoOverlap(vector<int>& nums, int firstLen, int secondLen) { int n = nums.size(); vector<int> prefixSum(n + 1, 0); // 计算前缀和数组 for (int i = 0; i < n; ++i) { prefixSum[i + 1] = prefixSum[i] + nums[i]; } // 初始化四个数组用于记录不同情况下的最大值 vector<int> firstMax(n, 0); // 记录到i位置为止firstLen长度的子数组最大值 vector<int> secondMax(n, 0); // 记录到i位置为止secondLen长度的子数组最大值 vector<int> firstMaxFromRight(n, 0); // 记录从i位置开始往右firstLen长度的子数组最大值 vector<int> secondMaxFromRight(n, 0); // 记录从i位置开始往右secondLen长度的子数组最大值 // 从左到右计算firstMax和secondMax for (int i = firstLen - 1; i < n; ++i) { int currentSum = prefixSum[i + 1] - prefixSum[i + 1 - firstLen]; if (i == firstLen - 1) { firstMax[i] = currentSum; } else { firstMax[i] = max(firstMax[i - 1], currentSum); } } for (int i = secondLen - 1; i < n; ++i) { int currentSum = prefixSum[i + 1] - prefixSum[i + 1 - secondLen]; if (i == secondLen - 1) { secondMax[i] = currentSum; } else { secondMax[i] = max(secondMax[i - 1], currentSum); } } // 从右到左计算firstMaxFromRight和secondMaxFromRight for (int i = n - firstLen; i >= 0; --i) { int currentSum = prefixSum[i + firstLen] - prefixSum[i]; if (i == n - firstLen) { firstMaxFromRight[i] = currentSum; } else { firstMaxFromRight[i] = max(firstMaxFromRight[i + 1], currentSum); } } for (int i = n - secondLen; i >= 0; --i) { int currentSum = prefixSum[i + secondLen] - prefixSum[i]; if (i == n - secondLen) { secondMaxFromRight[i] = currentSum; } else { secondMaxFromRight[i] = max(secondMaxFromRight[i + 1], currentSum); } } int result = 0; // 情况1:firstLen子数组在secondLen子数组左边 for (int i = firstLen - 1; i < n - secondLen; ++i) { result = max(result, firstMax[i] + secondMaxFromRight[i + 1]); } // 情况2:secondLen子数组在firstLen子数组左边 for (int i = secondLen - 1; i < n - firstLen; ++i) { result = max(result, secondMax[i] + firstMaxFromRight[i + 1]); } return result; } };
四、常见错误与调试技巧
新手在实现这类问题时容易犯以下错误:
边界条件处理不当:特别是数组越界问题
重叠判断错误:没有正确确保子数组不重叠
最大值更新逻辑错误:在构建辅助数组时更新条件不正确
调试建议:
使用小规模测试用例逐步验证
打印中间变量检查计算过程
特别注意边界索引的处理
五、总结
通过这道题目,我们学习了如何:
掌握这些技巧对于解决数组相关的算法问题至关重要。
原创内容 转载请注明出处