力扣560. 和为 K 的子数组

Posted weixin_43739821

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了力扣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]计算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)即可解决问题

以上是关于力扣560. 和为 K 的子数组的主要内容,如果未能解决你的问题,请参考以下文章

[LeetCode]560. 和为K的子数组(前缀和)

560. 和为 K 的子数组

560. 和为 K 的子数组

LeetCode-560. 和为 K 的子数组

题目地址(560. 和为 K 的子数组)

Leetcode 560.和为k的子数组