题目描述
给定一个无序数组arr, 其中元素可正、可负、可0。给定一个整数k,求arr所有子数组中累加和为k的最长子数组长度。
补充问题1:
给定一个无序数组 arr,其中元素可正、可负、可 0。求 arr 所有的子数组中
正数与负数个数相等的最长子数组长度。
具体实现:程序员代码面试指南—010未排序数组中累加和为给定值的最长子数组系列问题补1。
补充问题2:
给定一个无序数组 arr,其中元素只是 1 或 0。求 arr 所有的子数组中 0 和 1
个数相等的最长子数组长度。
具体实现:程序员代码面试指南—011未排序数组中累加和为给定值的最长子数组系列问题补2。
输入描述
第一行两个整数N, k。N表示数组长度,k的定义已在题目描述中给出
第二行N个整数表示数组内的数
输出描述
输出一个整数表示答案
示例
输入:
5 0
1 -2 1 1 1
输出:
3
解题思路
一开始使用双指针的滑动窗口来实现,但是发现题目中有负数出现,因此看了原书的解题思路,下面为书中的解题思路:
为了说明解法,先定义 s 的概念,s(i)代表子数组 arr[0…i]所有元素的累加和。
那么子数组arr[j…i] (0≤j≤i<arr.length)的累加和为 s(i)-s(j-1),因为根据定义,s(i)=arr[0…i]的累加和等于 arr[0…j-1]的累加和与 arr[j…i]的累加和相加,又有 arr[0…j-1]的累加和为 s(j-1)。所以,arr[j…i]的累加和为s(i)-s(j-1),这个结论是求解这道题的核心。
原问题解法只遍历一次 arr,具体过程为:
1.设置变量 sum=0,表示从 0 位置开始一直加到 i 位置所有元素的和。设置变量 len=0,
表示累加和为 k 的最长子数组长度。设置哈希表 map,其中,key 表示从 arr 最左边开始累加的过程中出现过的 sum 值,对应的 value 值则表示 sum 值最早出现的位置。
2.从左到右开始遍历,遍历的当前元素为 arr[i]。
1)令 sum=sum+arr[i],即之前所有元素的累加和 s(i),在 map 中查看是否存在 sum-k。
- 如果 sum-k 存在,从 map 中取出 sum-k 对应的 value 值,记为 j,j 代表从左到右不断累加的过程中第一次加出 sum-k 这个累加和的位置。根据之前得出的结论,arr[j+1…i]的累加和为 s(i)-s(j),此时 s(i)=sum,又有 s(j)=sum-k,所以 arr[j+1…i]的累加和为 k。同时因为 map 中只记录每一个累加和最早出现的位置,所以此时的 arr[j+1…i]是在必须以arr[i]结尾的所有子数组中,最长的累加和为 k 的子数组,如果该子数组的长度大于 len,就更新 len。
- 如果 sum-k 不存在,说明在必须以 arr[i]结尾的情况下没有累加和为 k 的子数组。
2)检查当前的 sum(即 s(i))是否在 map 中。如果不存在,说明此时的 sum 值是第一次出现的,就把记录(sum,i)加入到 map 中。如果 sum 存在,说明之前已经出现过 sum,map 只记录一个累加和最早出现的位置,所以此时什么记录也不加。
3)继续遍历下一个元素,直到所有的元素遍历完。
大体过程如上,但还有一个很重要的问题需要处理。根据 arr[j+1…i]的累加和为 s(i)-(j),所以,如果从 0 位置开始累加,会导致 j+1≥1。也就是说,所有从 0 位置开始的子数组都没有考虑过。所以,应该从-1 位置开始累加,也就是在遍历之前先把(0,-1)这个记录放进 map,这个记录的意义是如果任何一个数都不加时,累加和为 0。这样,从 0 位置开始的子数组就被我们考虑到了。
实现代码
/*
* @Description: 未排序数组中累加和为给定值的最长子数组长度
* @Author:
* @Date: 2020-10-23 19:12:55
* @LastEditTime: 2020-10-23 19:34:52
* @LastEditors: Please set LastEditors
*/
#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
int main(){
int N, k;
cin >> N >> k;
vector<int> arr(N);
for(int i = 0;i < N;i++){
cin >> arr[i];
}
map<int, int> m;
m.insert(pair<int, int>(0, -1));
int len = 0;
int sum = 0;
for(int i = 0;i < arr.size();i++){
sum += arr[i];
if(m.find(sum - k) != m.end()){
len = max(len, i - m.find(sum - k)->second);
}
if(m.find(sum) == m.end()){
m[sum] = i;
}
}
cout << len << endl;
//system("pause");
return 0;
}