题目描述
难度分:726
输入n(1≤n≤2×105),k(−1015≤k≤1015)和长为n的数组a(−109≤a[i]≤109)。
输出元素和等于k的连续子数组个数。
输入样例1
6 5
8 -3 5 7 0 -4
输出样例1
3
输入样例2
2 -1000000000000000
1000000000 -1000000000
输出样例2
0
算法
前缀和+哈希表计数
思路类似于“两数之和”,用一个哈希表mp记录每个前缀和的出现次数。遍历数组,如果遍历到i时,对应的前缀和为presum,并且它前面出现过mp[presum−k]次前缀和presum−k,那么这些位置j,就都满足子数组(j,i]的累加和为k,因此以i结尾累加和为k的子数组有mp[presum−k]个。
遍历完整个数组就能得到以任意一个元素结尾时能够产生的“累加和为k的子数组”数量。需要注意的是,在遍历之前要初始化mp[0]=1,因为刚开始一个元素都没有,只有一个0的前缀和。
复杂度分析
时间复杂度
遍历了一遍数组,中间的哈希表操作都是O(1)的,因此算法整体的时间复杂度为O(n)。
空间复杂度
哈希表是唯一的额外空间消耗,最差情况下每个位置都是一个不同的前缀和,因此额外空间复杂度为O(n)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long LL;
const int N = 200010;
int n, a[N];
LL k;
int main() {
scanf("%d%lld", &n, &k);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
unordered_map<LL, int> mp;
mp[0] = 1;
LL presum = 0, ans = 0;
for(int i = 1; i <= n; i++) {
presum += a[i];
LL x = presum - k;
if(mp.count(x)) ans += mp[x];
mp[presum]++;
}
printf("%lld\n", ans);
return 0;
}