当前位置: 首页 > news >正文

LintCode第1712题 - 和相同的二元子数组

描述

在由若干 0 和 1 组成的数组 A 中,有多少个和为 S 的非空子数组

样例 1:

输入:A = [1,0,1,0,1], S = 2
输出:4
解释:
如下面黑体所示,有 4 个满足题目要求的子数组:
[1,0,1]
[1,0,1]
[1,0,1,0]
[0,1,0,1]

样例 2:

输入:A = [0,0,0,0,0,0,1,0,0,0], S = 0
输出:27
解释:
和为 S 的子数组有 27 个

思路1 采用同向双指针法 通过暴力双指针枚举所有起点 + 滑动右指针

每轮固定左指针 left,右指针从 left 向右滑动; 每次累加和,如果等于 S,就记录这个子数组; 关键点是从每个 left 出发,向右滑,不能随便跳过任何一个组合

代码1:

public class Solution {

    public int numSubarraysWithSum(int[] A, int S) {

        int n=A.length;

        int count=0;

        for(int left = 0; left < n; left++)//同向双指针法计算是否为目前值S

        {

            int accumulatedNum = 0;

            for (int right = left; right < n; right++) {

                accumulatedNum += A[right];

                if (accumulatedNum == S) {

                    count++;

                } else if (accumulatedNum > S) {

                    break; // 再滑动右指针没意义了

                }

            }

           

        }

        return count;

    }

}

思路2 前缀和 + 哈希表

每轮固定左指针 left,右指针从 left 向右滑动; 每次累加和,如果等于 S,就记录这个子数组; 关键点是从每个 left 出发,向右滑,不能随便跳过任何一个组合

任意一个子数组 [i..j] 的和 = prefixSum[j] - prefixSum[i-1] 若要求 sum[i..j] = S,则等价于: prefixSum[j] - prefixSum[i-1] == S → prefixSum[i-1] == prefixSum[j] - S

prefixMap 来记录前缀和出现的次数,作用是:

键(Key)值(Value)
某个前缀和 x这个前缀和 x 出现过多少次

prefixMap.put(0, 1); 

前缀和为 0 出现了 1 次(这是初始化)

它允许我们从下标 0 开始的子数组也能被统计进来

accumulatedNum 当前前缀和,也就是从 A[0] 到 A[i] 的累加值

count 当前找到的子数组个数,最终返回这个值

步骤 1:累加前缀和 

accumulatedNum += A[i]; // 累加前缀和 将当前元素 A[i] 累加到 accumulatedNum 中; accumulatedNum 代表前缀和 prefixSum[i]

每次比较都是当前前缀和-s 当有

既等式

prefixSum[j] - prefixSum[i-1] == S
prefixSum[i-1] == prefixSum[j] - S
accumulatedNum - S

是我们想找的值 意思为在前面的位置出现过这样一个前缀和,
这样从那个位置之后到我当前位置,就刚好是一个和为 S 的子数组

prefixMap.get(accumulatedNum - S)之前有多少个位置,它们的前缀和等于 accumulatedNum - S

每一个都可以作为「合法子数组的起点」,从那个点到当前 i 的子数组和为 S
所以:prefixMap.get(accumulatedNum - S) 就是当前命中的合法子数组个数

prefixMap.put(accumulatedNum, prefixMap.getOrDefault(accumulatedNum, 0) + 1);
把当前的前缀和 accumulatedNum 的出现次数 +1

因为我们要统计「从前面某个位置 i 到当前位置 j 的和为 S 的子数组数量」。

每一个前缀和,都可能为后面提供组合,所以我们必须记录它出现的次数

import java.util.*;

public class Solution {
    public int numSubarraysWithSum(int[] A, int S) {
        int accumulatedNum = 0; // 当前的前缀和
        int count = 0;

        Map<Integer, Integer> prefixMap = new HashMap<>();
        prefixMap.put(0, 1); // 初始化:前缀和为 0 出现了 1 次

        for (int i = 0; i < A.length; i++) {
            accumulatedNum += A[i]; // 累加当前前缀和

            // 如果之前出现过 accumulatedNum - S,就说明找到了满足条件的子数组
            if (prefixMap.containsKey(accumulatedNum - S)) {
                count += prefixMap.get(accumulatedNum - S);
            }

            // 更新当前前缀和出现次数
            prefixMap.put(accumulatedNum, prefixMap.getOrDefault(accumulatedNum, 0) + 1);
        }

        return count;
    }
}   

一个疑问点是如果不写

那么就会漏掉那些从 index = 0 开始就满足和为 S 的子数组

例 A = [1, 0, 1], S = 2

有 prefixMap.put(0, 1) 的时候:

iA[i]accumulatedNumaccumulatedNum - SprefixMap命中count
011-10
101-10
21201

最终结果: 找到了子数组 [0, 1, 2],和为 2。

当prefixMap.put(0, 1)没有的时候 

当前累计为 2 的时候,你想找的前缀和是 0,但 prefixMap 里没有

prefixMap.getOrDefault(accumulatedNum, 0) 会取下标为0的值 因为没有放入1 则

这段合法子数组 [1, 0, 1] 就不会被统计 即错误结果 最终结果是:count=0

推荐刚开始学习使用双指针法 进阶使用前缀和 + 哈希表

相关文章:

  • (一)丶Windows安装RabbitMQ可能会遇到的问题
  • Docker入门篇4:查看容器资源、查看容器详细信息、查看容器日志、查看容器内运行的进程
  • uni-app jyf-parser将字符串转化为html 和 rich-text
  • HTML URL 学习笔记
  • 清洁机器人垃圾物识别与智能分类回收系统研究(大纲)
  • 调用链路传递隐式参数
  • 连接chatgpt的桌面语音助手
  • AD(Altium Designer)更换PCB文件的器件封装
  • ObjC属性assign/retain/copy的区别?不同编程语言的属性?ObjC语言obj->name和obj.name的区别?类成员变量?
  • javabean类,测试类,工具类都是什么?
  • 树莓派5介绍与系统安装
  • OpenCV vs MediaPipe:哪种方案更适合实时手势识别?
  • ubuntu桌面图标异常——主目录下的所有文件(如文档、下载等)全部显示在桌面
  • 【MLP多层感知机】
  • c++怎么将输入的一行字符根据“,“分割成字符串数组或者整型数组
  • @WebFilter 注解
  • DeepSeek处理多模态数据的技术要点和实现方式
  • 在CentOS系统上运行Ruby on Rails应用的详细步骤
  • Linux安装JDK
  • JavaScript基础-DOM事件流
  • 他比李白接地气,比杜甫乐观,比白居易刚毅
  • 以军称若停火谈判无进展,将大幅扩大加沙军事行动
  • 第152次中老缅泰湄公河联合巡逻执法行动圆满结束
  • 偷拍拷贝某轨道车技术信息后撰写论文发表,工程师被判一年有期徒刑
  • 共话城市自然之美,“微观黄浦”自媒体网络大V沙龙首场活动举行
  • 世联行:2024年营业收入下降27%,核心目标为“全面消除亏损公司和亏损项目”