程序员代码面试指南---009未排序数组中累加和为给定值的最长子数组长度

题目描述

给定一个无序数组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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值