算法应用之前缀和+哈希表

503 阅读2分钟

前言

今天来记录一种题型: 在一个数组中,找到和为target的最长子数组长度

观前须知:需要熟练掌握哈希表以及前缀和算法,并知道应该用于什么场景

例题

  1. 和等于 k 的最长子数组长度 给定一个数组 nums 和一个目标值 k,找到和等于 k 的最长子数组长度。如果不存在任意一个符合要求的子数组,则返回 0。

注意: nums 数组的总和是一定在 32 位有符号整数范围之内的。

示例 1:

输入: nums = [1, -1, 5, -2, 3], k = 3
输出: 4 
解释: 子数组 [1, -1, 5, -2] 和等于 3,且长度最长。

(题目出自LeetCode325题) 因为本题是会员题,有会员的朋友们可以去亲自做一下,没会员的可以本地试着写一下代码。

思路

正常来说,对于这类题目我们都是通过dp或者滑动窗口来解决的。但此题非彼题,我们发现滑动窗口(当右指针右移时,左指针不满足右移的单调性),dp的话也没有一种很好的递推方式。

对于这种题目,我们需要稍加转换一下思路:

  1. 求某一段区间和,我们可以用前缀和预处理,然后坐到O(1).
  2. 我们要找到的是最长的一段区间,使其和等于target。即s[i] - s[j] = target且i - j尽可能的大。
  3. 我们先不考虑i - j尽可能的大,我们先想想如何才能在数组中找到s[i] - s[j] = target呢?其实是LeetCode第一题 —— 两数之和。即s[i] + (-s[j]) = target.
  4. 到此,我们就距离答案只有一步之遥,也就是让i尽可能的离j远。我们可以这样做,存hash值的时候,如果hash表中已经存在对应的key,那我们就不要存(因为我们要保留前面的数)。

AC代码

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var maxSubArrayLen = function(nums, k) {
    const n = nums.length;
    const hash = {0: 0};
    
    const sum = Array(n + 1).fill(0);
    for(let i = 1; i <= n; ++ i) sum[i] = sum[i - 1] + nums[i - 1];

    let ans = 0;
    for(let i = 1; i <= n; ++ i) {
        if(hash[sum[i] - k] !== void 0) 
            ans = Math.max(ans, i - hash[sum[i] - k]);
        if(hash[sum[i]] === void 0) hash[sum[i]] = i; 
    }

    return ans;
};