【题干】
给你一个整数数组 nums
和一个整数 k
,统计并返回 该数组中和为 k
的子数组的个数 。
子数组是数组中元素的连续非空序列。
【思路】
拿到题第一反应是滑动窗口,写到一半才意识到问题,在没有非负条件的情况下窗口左右边界的移动并不能保证子数组和的减小和增大,滑动窗口失去意义。
从三重循环暴力解(遍历左边界i,遍历右边界j,遍历i到j求和)出发,思考优化方法:
- 用前缀和+差分优化重复的求和过程,在O(1)时间获取想要的子数组和。
- 考虑到返回的是个数不是位置,用hash map直接统计前缀和的分布。
- 由于对每个给定元素进行检验时,都只需要用到其左侧的信息,可以将求和和检验过程放在一次遍历中完成。
优化后的思路如下:
- 对每一个元素nums[i]求出其前缀和sum
- 任意子数组[i,j]的和为j的sum减去i-1的sum,特别的,当i为0时,补充定义i-1的前缀和0
- 用hash map存储当前前缀和出现的次数
- 用cnt存储当前和为k的子数组总数
- 检查当前已存储的前缀和中,值为sum-k的和出现的次数,aka满足区间和为k的子数组次数,将其加到cnt
- 将当前前缀和加入map(或增加次数)
【题解】
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int cnt = 0;
unordered_map<int, int> map;
map[0] = 1;
int sum = 0;
for (auto num : nums)
{
sum += num;
//检查前缀和中与该sum差距为k的和的数量
cnt += map[sum - k];
//将当前sum放入hash中
++map[sum];
}
return cnt;
}
};