LeetCode 算法:使数组和能被 P 整除

184 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 1 天,点击查看活动详情

使数组和能被 P 整除

原题地址

给你一个正整数数组 nums,请你移除 最短 子数组(可以为 ),使得剩余元素的  能被 p 整除。 不允许 将整个数组都移除。

请你返回你需要移除的最短子数组的长度,如果无法满足题目要求,返回 -1 。

子数组 定义为原数组中连续的一组元素。

示例 1:

输入:nums = [3,1,4,2], p = 6
输出:1
解释:nums 中元素和为 10,不能被 p 整除。我们可以移除子数组 [4] ,剩余元素的和为 6

示例 2:

输入:nums = [6,3,5,2], p = 9
输出:2
解释:我们无法移除任何一个元素使得和被 9 整除,最优方案是移除子数组 [5,2] ,剩余元素为 [6,3],和为 9

示例 3:

输入:nums = [1,2,3], p = 3
输出:0
解释:和恰好为 6 ,已经能被 3 整除了。所以我们不需要移除任何元素。

示例  4:

输入:nums = [1,2,3], p = 7
输出:-1
解释:没有任何方案使得移除子数组后剩余元素的和被 7 整除。

示例 5:

输入:nums = [1000000000,1000000000,1000000000], p = 3
输出:0

提示:

  • 1 <= nums.length <=10510^5
  • 1 <= nums[i] <=10910^9
  • 1 <= p <=10910^9

思路分析

  1. 以数组[a,b,c,d]为例,和为sumreminder为数组和与目标元素 p 相除得到的余数来分析题目;
  2. reminder0,则证明能被目标元素整除,则不需要移除子数组,返回 0 即可;
  3. reminder 不为 0,则需要寻找如何移除子数组才可以整除;
  4. sum1 = a + b + csum2 = a + b + c + d,若 sum1 % p === sum2 % p,那么 (sum1 - sum2) % p = 0,也就是 sum2 中移除 sum1 后可以整除;
  5. 按照步骤4的推论,可以得知,若 sumi % p === (sumj - reminder) % p 则说明 (i,j] 中存在移除某个子数组后能被 p 整除的子数组,因此遍历数组寻找最小的子数组返回其长度即可。

AC 代码

/**
 * @param {number[]} nums
 * @param {number} p
 * @return {number}
 */
var minSubarray = function(nums, p) {
    const len = nums.length
    let sum = 0
    for(let i = 0; i < len; i++) {
        sum += nums[i]
    }

    const reminder = sum % p
    if(reminder === 0) {
        return 0
    }
    
    const map = new Map
    let currentSum = 0
    let res = Infinity
    map.set(0, -1)
    for(let i = 0; i < len; i++) { 
        currentSum += nums[i]
        const key = (currentSum - reminder + p) % p
        if(map.has(key)) {
            res = Math.min(res, i - map.get(key))
            if( res === 1 && len > 1 ){
                return res
            }
        }
        map.set(currentSum % p, i)
    }
    if(res >= len) res = Infinity
    return res === Infinity ? -1 : res
};

结果:

  • 执行结果: 通过
  • 执行用时:100 ms, 在所有 JavaScript 提交中击败了96.00%的用户
  • 内存消耗:60.1 MB, 在所有 JavaScript 提交中击败了96.00%的用户
  • 通过测试用例:142 / 142

END