每日一题:930. 和相同的二元子数组

题目:

给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个和为 goal 的 非空 子数组。

子数组 是数组的一段连续部分。

示例 1:

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

输入:nums = [0,0,0,0,0], goal = 0
输出:15
 

提示:

1 <= nums.length <= 3 * 104
nums[i] 不是 0 就是 1
0 <= goal <= nums.length

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-subarrays-with-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路:

1. 暴力求解:

用两层循环找到所有子数组并检查他们的和是否符合条件,符合条件就给ans增加1.

这个方法显然不行因为数据量太大了。O(n^2)肯定超时。 

2. 使用前缀和

我们可以使用前缀和来储存子数组的和。在得到所有子数组的和之后,遍历不同子数组并算出他们的差是否满足要求,如果满足,res加一。这里参考大佬labuladong的知乎文章:前缀和技巧。这里的两层遍历可以看作是求一个不断变化的区间的和并检查这些和是否符合条件。(sum[i]-sum[j] == k?)

代码:

// https://zhuanlan.zhihu.com/p/107778275
int subarraySum(int[] nums, int k) {
    int n = nums.length;
    // 构造前缀和
    int[] sum = new int[n + 1];
    sum[0] = 0; 
    for (int i = 0; i < n; i++)
        sum[i + 1] = sum[i] + nums[i];

    int ans = 0;
    // 穷举所有子数组
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < i; j++)
            // sum of nums[j..i-1]
            if (sum[i] - sum[j] == k)
                ans++;

    return ans;
}

这样的算法也会因为两层循环让时间复杂度有O(n^2),所以也会超时。但是我们可以在这个的基础上进行优化。注意这里的sum[i]-sum[j]==k可以转换成sum[i]-k

3.优化的前缀和

因为要返回的是符合要求的子数组的总数,我们可以用一个map来储存前缀和出现的次数并简化上述的两层循环

代码:

class Solution {
public:
    int numSubarraysWithSum(vector<int>& nums, int goal) {
        int res;
        map<int, int> count;
        count[0] = 1;
        int sumSoFar = 0;
        
        for (int num:nums)
        {
            sumSoFar += num;
            // sumSoFar-goal is where to find the bound such that sumSoFar-this value = goal
            res += count[sumSoFar - goal];
            count[sumSoFar] ++;
        }

        return res;
    }
};

结果:

执行用时:72 ms, 在所有 C++ 提交中击败了45.01%的用户

内存消耗:37.1 MB, 在所有 C++ 提交中击败了5.13%的用户

参考:

1. 前缀和技巧, labuladong,https://zhuanlan.zhihu.com/p/107778275

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值