LeetCode——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 存入哈希表,以便后续计算。

说明:

  1. prefixSum[i] = nums[0] + nums[1] + ... + nums[i], prefixSum[i] 代表从 0 到 i 位置的 累积和。
  2. 任何子数组 nums[l] ... nums[r] 的和等于:sum = prefixSum[r] - prefixSum[l-1]
  3. prefixSum[r] - k == prefixSum[l-1]。说明 如果我们在前面某个位置l-1计算过 prefixSum[l-1],那么 prefixSum[r] - k 应该等于 prefixSum[l-1],这就意味着找到了一个和为 k 的子数组。
  4. 假设 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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值