每日一题: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
标签:二元,nums,int,前缀,sum,数组,930,goal 来源: https://blog.csdn.net/weixin_47109266/article/details/118565896