题目描述

✅ 930. 和相同的二元子数组

image-20260929105231892

题意分析

数组只包含 0 和 1,统计元素和恰好为 goal 的非空连续子数组数量。即使元素序列相同,只要左右端点不同,就算不同子数组,因此要记录满足条件的端点组合数量。

解法:前缀和 + 哈希计数

核心思路

[!blue]

定义 P[t] 为前 t 个元素的和,且 P[0]=0。右端点为 r、左端点为 l 的区间和等于 P[r+1]-P[l];要让它等于 goal,就需要此前存在 P[l]=P[r+1]-goal。

扫描当前元素后,用 sum 表示 P[r+1],哈希表 count 保存此前各前缀和出现的次数。每出现一次 sum-goal,就对应一个不同的合法左端点,因此把 count[sum-goal] 加入答案。零可能使多个前缀和相同,所以必须保存次数,不能只记录是否出现。

初始先登记空前缀 0 一次,对应 l=0,从而覆盖从数组开头开始的区间。每轮先查询旧表,再登记当前 sum,保证表中配对的前缀位置严格早于当前前缀,得到的区间一定非空;当 goal=0 时,这个顺序尤其不能颠倒。

每个合法子数组都会在扫描到它的右端点时,由其左端点对应的前缀计入一次。不同右端点分属不同轮,同一轮又按不同左端点的次数计数,因此既不会遗漏,也不会重复统计。

解题步骤

  1. 初始化前缀频次 {0:1}。
  2. 读入元素并更新当前前缀和。
  3. 将此前 sum-goal 的出现次数加入答案。
  4. 登记当前前缀供后续位置使用。

没有出现所需前缀和时,本轮贡献为零。goal=0 时可以正常统计连续零构成的区间;重复前缀会逐次增加可选左端点数量,无需额外分支。

代码实现

class Solution {
    public int numSubarraysWithSum(int[] nums, int goal) {
        Map<Integer, Integer> count = new HashMap<>();

        // 空前缀登记一次,覆盖从数组开头开始的合法区间。
        count.put(0, 1);

        int sum = 0;
        int answer = 0;

        for (int x : nums) {
            sum += x;
            // 先统计更早前缀,再登记当前值,避免把自身配成空区间。
            answer += count.getOrDefault(sum - goal, 0);
            count.put(sum, count.getOrDefault(sum, 0) + 1);
        }

        return answer;
    }
}
func numSubarraysWithSum(nums []int, goal int) int {
    // 空前缀登记一次,覆盖从数组开头开始的合法区间。
    count := map[int]int{0: 1}
    sum := 0
    answer := 0

    for _, x := range nums {
        sum += x
        // 先统计更早前缀,再登记当前值,避免把自身配成空区间。
        answer += count[sum-goal]
        count[sum]++
    }
    return answer
}

复杂度分析

  • 时间复杂度:哈希操作按均摊常数计为 $O(n)$。
  • 空间复杂度:$O(n)$,保存前缀频次。

关键点总结

[!green]

  • 表中保存次数,不是单个下标或是否出现。
  • 先查询再登记,排除零长度区间。
  • 相同前缀和可以多次出现,每次都对应不同左端点。

易错点总结

[!yellow]

  • 遗漏空前缀,会漏掉从下标零开始的区间。
  • 先登记再查询,在 goal=0 时会把当前前缀和自身配成空区间。
  • 把频次覆盖为一,会丢失对应同一前缀和的其他左端点。
  • 只判断 sum 是否等于 goal,只能发现从开头开始的区间。

相似题目

题目 难度 关联与区别
560. 和为 K 的子数组 中等 前缀和频次法可直接处理任意整数,本题二元非负性还允许用至多目标和的窗口计数相减。
1248. 统计「优美子数组」 中等 把奇数映射成1、偶数映射成0后,原题就变成指定和的二元子数组计数。
325. 和等于 k 的最长子数组长度 中等 前缀和配合哈希表查找所需历史前缀;本题二进制数组上统计目标和,该题存最早前缀下标以最大化长度。
437. 路径总和 III 中等 前缀和配合哈希表查找所需历史前缀;本题二进制数组上统计目标和,该题沿树路径维护前缀次数并回溯恢复。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2021/87881998
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!