注意要考虑负数
直接dfs超时:
class Solution {
public:
int res = 0;
int subarraySum(vector<int>& nums, int k) {
for(int i = 0; i < nums.size(); ++i)
dfs(nums, k, i, nums[i]);
return res;
}
void dfs(vector<int>& nums, int k, int idx, int sum){
if(k == sum){
++res;
}
if(idx == nums.size()-1) return;
dfs(nums, k, idx+1, sum+nums[idx+1]);
}
};
两处for循环暴力枚举(效率一般),先固定右边界i,遍历左边界j:
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int res = 0;
for(int i = 0; i < nums.size(); ++i){
int sum = 0;
//考虑以i为结尾的连续子数组,那么开头可能是i之前的每一个数,组要枚举
for(int j = i; j >= 0; --j){//注意这儿如果从前往后的话就会有很多重复计算
sum += nums[j];
if(sum == k) ++res;
}
}
return res;
}
};
当然也可以先固定左边界,枚举右边界:
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int res = 0, n = nums.size();
for(int i = 0; i < n; ++i){
int sum = 0;
//i为左边界
for(int j = i; j < n; ++j){//枚举右边界
sum += nums[j];
if(sum == k) ++res;
}
}
return res;
}
};
计算连续的和,想到了前缀和+哈希表的思路:
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int res = 0, sum = 0;
unordered_map<int, int> m;//<前缀和,出现次数>
m[0] = 1;//初始值,前缀和为0,代表没有元素的时候
for(const auto &num : nums){
sum += num;//前缀和
if(m.count(sum-k)) res += m[sum-k];
++m[sum];
}
return res;
}
};
换种写法,可能更好理解:
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int res = 0;
vector<int> presum(nums.size()+1, 0);
//前缀和,第一个为0,代表没有元素
for(int i = 1; i < presum.size(); ++i) presum[i] = presum[i-1] + nums[i-1];
//for(auto c : presum) cout << c << " ";
unordered_map<int, int> m;//<前缀和,出现次数>
for(const auto &v : presum){
if(m.count(v-k)) res += m[v-k];
++m[v];
}
return res;
}
};