LeetCode——560. 和为 K 的子数组
[560. 和为 K 的子数组]
- 分析:
- 正确的解法:前缀和 + 哈希表
(https://leetcode.cn/problems/subarray-sum-equals-k/)
已解答
中等
相关标签
相关企业
提示
给你一个整数数组 nums
和一个整数 k
,请你统计并返回 该数组中和为 k
的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
输入:nums = [1,1,1], k = 2
输出:2
示例 2:
输入:nums = [1,2,3], k = 3
输出:2
提示:
1 <= nums.length <= 2 * 104
-1000 <= nums[i] <= 1000
-107 <= k <= 107
分析:
没注意到要连续,好吧,我当成了背包问题。。。。附上代码(错误版本)
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
//vector<vector<int>> dp(nums.size(),0);
vector<vector<int>> dp(nums.size(), vector<int>(k+1, 0));
for(int i=0;i<=k;i++){
if(nums[0]==i)
dp[0][i]=1;
}
for(int i=0;i<nums.size();i++){
dp[i][0]=1;
}
dp[0][0]=1;
for(int i=1;i<nums.size();i++){
for(int j=1;j<=k;j++){
if(j-nums[i]>=0)
dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i]];
else
dp[i][j]=dp[i-1][j];
}
}
return dp[nums.size()-1][k];
}
};
方法又用错了,滑动窗口方法无法用于包含负数的队列。解答错误35 / 93 个通过的测试用例,提交于 2025.03.12 09:32
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
if(nums.size()==0)
return 0;
int i=0,j=1;
int sum=nums[0];
int ans=0;
if(nums.size()==1&&sum==k){
return 1;
}
while(i<nums.size()&&j<nums.size()){
if(sum<k){
sum+=nums[j];
j++;
}
if(sum==k){
ans++;
sum-=nums[i];
i++;
}
if(sum>k){
sum-=nums[i];
i++;
}
if(sum==k){
ans++;
sum-=nums[i];
i++;
}
}
while(i<nums.size()){
if(sum==k){
ans++;
}
sum-=nums[i];
i++;
}
return ans;
}
};
等于是,正数的样例被我通过了,带有负数的样例,没通过。
这个问题不能使用滑动窗口,因为 nums
可能包含负数,导致窗口无法通过简单的收缩/扩展来找到正确的子数组。
例如,[1, -1, 1]
,如果 k=1
,滑动窗口会错误地跳过某些子数组。
正确的解法:前缀和 + 哈希表
我们可以用 前缀和 + 哈希表 来高效解决这个问题。
思路:
- 维护一个
prefixSum
(表示从数组起点到当前索引的和)。 - 使用
unordered_map<int, int>
记录前缀和出现的次数。 - 计算当前
prefixSum
时,检查prefixSum - k
是否存在于哈希表中,若存在,则说明前面某一段子数组的和为k
,增加计数。 - 将
prefixSum
存入哈希表,以便后续计算。
说明:
prefixSum[i] = nums[0] + nums[1] + ... + nums[i]
,prefixSum[i]
代表从 0 到 i 位置的 累积和。- 任何子数组
nums[l] ... nums[r]
的和等于:`sum = prefixSum[r] - prefixSum[l-1]`` - ``prefixSum[r] - k == prefixSum[l-1]
。说明 如果我们在前面某个位置
l-1计算过
prefixSum[l-1],那么
prefixSum[r] - k应该等于
prefixSum[l-1]`,这就意味着找到了一个和为 k 的子数组。 - 假设
prefixSum - k
之前出现了 多次,那么说明有 多个子数组 的和等于k
,我们需要把这些子数组全部计入答案。所以prefixSumCount
需要存次数,而不是简单存前缀和
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int,int> preSumCount;
preSumCount[0]=1;
int preSum=0;
int ans=0;
for(auto& num:nums){
preSum+=num;
if(preSumCount.find(preSum-k)!=preSumCount.end()){
ans+=preSumCount[preSum-k];
}
preSumCount[preSum]++;
}
return ans;
}
};