什么是前缀和
PreSum[i] = nums[i] + nums[i - 1] +…+ nums[1] 表示i的前缀和
前缀和数组对连续子数组求和问题基本上都能用上。
560.和为k的子数组
思路
pre[i] = pre[i − 1] + nums[ i ]
A[i] + A[i+1] +… A[j] = B[i] - B[j - 1]
那么令A[i] + A[i+1] +… A[j]这个子数组和为 k 这个条件我们可以转化为
B[i] − B[j − 1] == k
B[j − 1] == B[ i ] − k
我们已知 k 和前缀 B[i] 的值, 这个时候只需要用hash判断是否有 B[j - 1]即可
// 方法1
public int subarraySum(int[] nums, int k) {
int[] preSum = new int[nums.length + 1];
// 这里注意, 最好是nums.length + 1的长度, 第一个为0
preSum[0] = 0;
// 计算前缀和
for (int i = 0; i < nums.length; i++) {
preSum[i + 1] = preSum[i] + nums[i];
}
int count = 0;
for (int i = 0; i < nums.length; i++) {
for (int j = i; j < nums.length; j++) {
// preSum[j + 1] - preSum[i] = preSum[j + 1 - i] == k
if (preSum[j + 1] - preSum[i] == k) {
count++;
}
}
}
return count;
}
// 方法2
int subarraySum(vector<int>& nums, int k) {
int len = nums.size();
unordered_map<int, int> map;
map[0] = 1;
int ans = 0;
int preSum = 0;
for (int i = 0; i < len; i++) {
//计算前缀
preSum += nums[i];
//查看是否存在一个子数组, 让preSum[i] - preSum[j - 1] = preSum[j - i] == k
ans += map[preSum - k];
map[preSum]++;
}
return ans;
}
523. 连续的子数组和
思路
连续子数组和为k的倍数,可以对前缀和 sum 进行取余,取余之后的 sum 必然小于k,此时在map中寻找, 如果找到了说明之前已经有了一个前缀和等于 sum的情况,则说明两个位置中间的和必为 0
比如[3,2,4,5] k = 6, 那么3+2+4 % k 的结果和3%k结果相同,那么意味着left-i之间至少有个k
bool checkSubarraySum(vector<int>& nums, int k) {
map<int, int> mp;
mp[0] = -1;
int sum = 0;
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
if (k != 0) sum %= k;
if (mp.find(sum) != mp.end()) {
//需要大小至少为2
if (i - mp[sum] > 1) return true;
} else {
mp[sum] = i;
}
}
return false;
}