平均数为k的最长连续子数组

849 阅读3分钟

题目

  • 给定n个正整数组成的数组,求平均数正好等于 k 的最长连续子数组的长度。

输入描述

  • 第一行输入两个正整数n和k,用空格隔开。
  • 第二行输入n个正整数ai,用来表示数组。 1≤n≤200000 1≤k,ai≤10^9

输出描述

如果不存在任何一个连续子数组的平均数等于k,则输出-1。
否则输出平均数正好等于 k 的最长连续子数组的长度

示例

  • 输入
5 2
1 3 2 4 1
  • 输出
3
  • 说明 取前三个数即可,平均数为2。

思路

暴力思路

  • 卡大数据的样例,按照题目的思路:将所有可以组合的区间都计算一遍,除以其中包含的元素个数

学习思路

  • 问题转化:多个数相加可以整除k,那么可以将数组中的每一个元素减去k,得到的数组中的元素相加为0.
    • 此时,就将问题转化为:找到数组中连续的子数组和为0的最长长度
    • 还可以再转换为:寻找区间为0的最长连续子区间
  • 说到“子区间”,那么一定不要将所有全部的区间都枚举一遍,尽量的将计算过的利用起来,避免重复计算
    • 考虑:前缀和方法,自然也就是连续的子区间
    • 通过前缀和将区间的值记录下来,需要哪个区间的值,直接作差即可
  • 那么如何才能找到区间和为0的区间呢?
    • 2个区间值是相同的和,作差后,剩余下的区间的值即为0
  • 举例:数组:[4, 2, 7, 6] k = 5
    • 步骤一:给数组所有元素统一减去k,得到数组:[-1, -3, 2, 1]。此时我们可以分析针对这个数组中后半部分[-3, 2, 1]最长的连续子区间,且总和为0;验证原数组:2 +7 +6 = 15 / 3 = 5,是符合要求的
    • 步骤二:针对减去k后,得到的数组,计算前缀和,前缀和数组 = [0, -1, -4, -2, -1](注:区间和第一个元素为0,对下标的维护),其中区间结果为-1的出现了2次,即:数组中第0(下标)个数的区间 与 数组中第3(下标)个数到第0个数的区间,2个区间相减,剩下区间内第3、2、1的元素,即:最长的连续区间长度为3

实现代码

const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;

void async function () {
    // Write your code here
    let n,k,tokens;
    while(line = await readline()){
        tokens = line.split(' ');
        if(!n || !k) {
            n = tokens[0],k = tokens[1];
        }
    }
    // 转换为相加为0的数组解决问题
    tokens = tokens.map(item => {
        return Number(item) - k;
    })
    console.log(tokens)
    // 计算前缀和,使用哈希表记录前缀和的数值,相同的就说明区间为0
    let ans = -1, map = new Map(), len = tokens.length, pre = 0;
    map.set(0, 0)
    for(let i = 1; i <= len; i++) {
        pre = tokens[i - 1] + pre;
        console.log(pre)
        if(map.has(pre)){
           ans = Math.max(ans, i - map.get(pre))
        }else {
            map.set(pre, i);
        }
    }
    console.log(ans)
}()

考察

  • 前缀和+哈希