编码母题 / P0 / 经典算法 / 12

和为K的连续子数组

重要程度必练难度中等预计完成25–35 分钟

题目是什么

输入可含正数、负数和零的整数数组 nums,以及整数 k;返回元素和恰好等于 k 的非空连续子数组个数,不是返回子数组本身;空数组返回 0。

输入输出示例

nums=[1,-1,0]、k=0 时,合法区间为 [1,-1]、[0]、[1,-1,0],返回 3。

练习起手式与验证

/*
 * 题目:和为K的连续子数组
 * 要求:输入可含正数、负数和零的整数数组 nums,以及整数 k;返回元素和恰好等于 k 的非空连续子数组个数,不是返回子数组本身;空数组返回 0。
 * 示例:nums=[1,-1,0]、k=0 时,合法区间为 [1,-1]、[0]、[1,-1,0],返回 3。
 */
function countSubarraysWithSum(nums, k) {}
console.log(countSubarraysWithSum([1, 1, 1], 2)); // 预期:2
console.log(countSubarraysWithSum([1, -1, 0], 0)); // 预期:3
console.log(countSubarraysWithSum([0, 0], 0)); // 预期:3
console.log(countSubarraysWithSum([], 0)); // 预期:0

解题思路与实现

展开解题思路

当前前缀和为 s 时,之前每个值为 s-k 的前缀都对应一个合法子数组。

function countSubarraysWithSum(nums, k) {
  const frequencies = new Map([[0, 1]]);
  let prefix = 0;
  let count = 0;
  for (const value of nums) {
    prefix += value;
    count += frequencies.get(prefix - k) ?? 0;
    frequencies.set(prefix, (frequencies.get(prefix) ?? 0) + 1);
  }
  return count;
}

// 三组以上可直接运行的验证。
console.log(countSubarraysWithSum([1, 1, 1], 2)); // 预期:2
console.log(countSubarraysWithSum([1, -1, 0], 0)); // 预期:3
console.log(countSubarraysWithSum([0, 0], 0)); // 预期:3
console.log(countSubarraysWithSum([], 0)); // 预期:0

时间复杂度:O(n)。每个元素只更新一次前缀和与哈希频次。

空间复杂度:O(n)。不同前缀和最多有 n+1 个。

下载原始 JS 文件