Problem: 560. 和为 K 的子数组
Problem: 53. 最大子数组和
思路
对于一个序列求解问题,我们可以先从两个维度思考:要不要排序,是否求解连续子序列。
这两道题都是求连续子序列问题。对于这一类拥有连续性质问题,滑动窗口、前缀和等方法往往可以进行时间上的优化。
解题方法
前缀和则可以将最内层遍历转换为简单的做差,这样做的好处不仅在于可以降低 n 的时间复杂度,还在于可以列出子序列和的计算公式,如此可以通过数学的方式进行三个值的相互求解。
例如对 [560. 和为 K 的子数组],此时的目标值——子序列和已经给出为k,需要寻找目标子序列。我们便可以移项,通过确定的子序列和和前缀和搜索子序列,从而求解。
同样的对 [53. 最大子数组和],我们通过前缀和公式,将原本对于子序列和最大值的比较转换为对前缀和最小值的比较,在不进行子序列和计算的前提下预知计算结果的大小关系,从而极大地降低了时间复杂度。
Code
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int ans = 0;
int pre = 0;
unordered_map<int, int> pre_m; // 前缀和为key的个数
pre_m[0] = 1;
for(auto& num : nums) {
pre += num;
if (pre_m.find(pre - k) != pre_m.end()) {
ans += pre_m[pre - k];
}
pre_m[pre] = pre_m.count(pre) ? pre_m[pre] + 1 : 1;
}
return ans;
}
};
class Solution {
const int MIN = -10001;
public:
int maxSubArray(vector<int>& nums) {
priority_queue<int, vector<int>, greater<int>> q;
q.push(0);
int pre = 0;
int res = MIN;
for(int i = 0; i < nums.size(); i++) {
pre = pre + nums[i];
res = max(res, pre - q.top());
q.push(pre);
}
return res;
}
};