力扣560. 和为 K 的子数组

题目描述:
给你一个整数数组 nums 和一个整数 k ,请你统计并返回该数组中和为 k 的连续子数组的个数。

示例 1:

输入:nums = [1,1,1], k = 2
输出:2

示例 2:

输入:nums = [1,2,3], k = 3
输出:2

提示:

1 <= nums.length <= 2 * 10^4
-1000 <= nums[i] <= 1000
-10^7 <= k <= 10^7

错误思路(可不看) 第一眼看到有点懵,没做过类似的,初次觉得用滑动窗口思想,但是k和nums中均有负数所以不符合.后面想到用暴力,但是暴力可以达到O(n^3)肯定超时,于是想着在暴力基础上优化,借dp思想想到优化思路,用dp[i][j]表示下标i到j-1(包括i和j-1下标)的元素和 这样dp[i][j]=dp[0][j]-dp[0][i]
但是还是超时了,时间也有O(n^2),错误代码:

int subarraySum(vector<int>& nums, int k) {
	int n = nums.size(),sum=0;
	vector<vector<int>>dp(n, vector<int>(n+1));//dp[i][j]表示下标i到j-1(包括i和j-1下标)的元素和 这样dp[i][j]=dp[0][j]-dp[0][i]
	dp[0][1] = nums[0];
	if (dp[0][1] == k) sum++;
	for (int i = 2; i <= n; i++) {
		dp[0][i] = dp[0][i - 1] + nums[i - 1];
		if (dp[0][i] == k) sum++;
	}
	for (int i = 1; i < n; i++) {
		for (int j = i + 1; j <= n; j++) {
			dp[i][j] = dp[0][j] - dp[0][i];
			if (dp[i][j] == k) sum++;
		}
	}
	return sum;
}

其实完全不用dp数组,直接二重循环i从0~n-1 j从i->0遍历,每次遍历用上次循环的和加上nums[j]统计个数也能达到O(n^2),空间O(1)。

正确思路:
用前缀和pre[i]表示0~i的元素和 则以i为结尾的元素和:pre[i]-pre[j-1]表示j~i的元素和 即nums[j…i]
则其中符合要求的元素应该是:pre[i]-pre[j-1]=k 所以pre[j-1]=pre[i]-k
所以对于以i为结尾的元素 我们只要数一数pre[0]~pre[i-1]里面有几个值为pre[i]-k,再判断pre[i]是否为k 就可以得出以i为结尾的元素有多少个符合要求。

ps: 上面的这些判断的边界情况我再具体解释一下:
判断pre[i-1]是否等于pre[i]-k,是判断以nums[i]自己是否等于k的情况,即一个元素组成的子数组;
判断pre[0]是否等于pre[i]-k,是判断nums[1…i]是否等于k的情况;
判断pre[i]是否等于k,是判断nums[0…i]是否等于k的情况,这个条件的判断最容易忽略。

那么我们可以从0~n-1遍历nums数组, 然后每次遍历到i 就通过pre[i-1]+nums[i]=pre[i]计算pre[i],然后将所有的前缀和统统放入map,key保存pre[i],value则保存这个pre[i]的出现次数。
这样我们可以以O(1)的时间来获得pre[0]~pre[i-1]里面有几个值为k 但是这里问题又来了 我们的map里怎么保证里面所有的key都是pre[0~i-1]呢?
要不然我们得到pre[i+5]也是=pre[i]-k,但是我们要求的是以i为结尾,这个显然不是
这个问题很好解决,我们存入pre[i]的前一步就计算以i为结尾的元素有多少个符合要求,这样map里就不会有大于i的键值,即所有map的key都是0~i-1

优化:因为计算pre[i]只用到pre[i-1] 所以pre可以不用数组存储 用一个单位就行了

正确代码:

int subarraySum(vector<int>& nums, int k) {
	unordered_map<int, int>mymap;//因为我们只需存取,不用排序所以用unordered更快
	int n = nums.size(),pre=nums[0],sum=0;//sum存返回值,,即符合条件个数
	mymap[pre] = 1;
	if (nums[0] == k) sum++;
	for (int i = 1; i < n; i++) {
		pre += nums[i];
		if (pre == k) sum++;
		sum += mymap[pre - k];
		mymap[pre]++;
	}
	return sum;
}

空间和时间O(n)即可解决问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值