algorithm-base/gif-algorithm/前缀和/leetcode974和可被K整除的子数组.md
2021-03-18 09:28:12 +08:00

3.8 KiB
Raw Blame History

leetcode 974 和可被 K 整除的子数组

题目描述

给定一个整数数组 A返回其中元素之和可被 K 整除的(连续、非空)子数组的数目。

示例:

输入A = [4,5,0,-2,-3,1], K = 5 输出7

解释:

有 7 个子数组满足其元素之和可被 K = 5 整除: [4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]

前缀和+HashMap

解析

我们在该文的第一题 **和为K的子数组 **中,我们需要求出满足条件的区间,见下图

微信截图_20210115194113

我们需要找到满足,和为 K 的区间。我们此时 presum 是已知的k 也是已知的,我们只需要找到 presum - k区间的个数就能得到 k 的区间个数。但是我们在当前题目中应该怎么做呢?见下图。

微信截图_20210115150520

我们在之前的例子中说到presum[j+1] - presum[i] 可以得到 nums[i] + nums[i+1]+.... nums[j],也就是[i,j]区间的和。

那么我们想要判断区间 [i,j] 的和是否能整除 K也就是上图中紫色那一段是否能整除 K那么我们只需判断

(presum[j+1] - presum[i] ) % k 是否等于 0 即可,

我们假设 (presum[j+1] - presum[i] ) % k == 0

presum[j+1] % k - presum[i] % k == 0;

presum[j +1] % k = presum[i] % k ;

我们 presum[j +1] % k 的值 key 是已知的,则是当前的 presum 和 k 的关系,我们只需要知道之前的前缀区间里含有相同余数 key的个数。则能够知道当前能够整除 K 的区间个数。见下图

微信截图_20210115152113

题目代码

class Solution {
    public int subarraysDivByK(int[] A, int K) {
        HashMap<Integer,Integer> map = new HashMap<>();
        map.put(0,1);
        int presum = 0;
        int count = 0;
        for (int x : A) {
             presum += x;
             //当前 presum 与 K的关系余数是几当被除数为负数时取模结果为负数需要纠正
             int key = (presum % K + K) % K;
             //查询哈希表获取之前key也就是余数的次数
             if (map.containsKey(key)) {
                 count += map.get(key);
             }
             //存入哈希表当前key也就是余数
             map.put(key,map.getOrDefault(key,0)+1);
        }
        return count;
    }
}

我们看到上面代码中有一段代码是这样的

int key = (presum % K + K) % K;

这是为什么呢?不能直接用 presum % k 吗?

这是因为当我们 presum 为负数时,需要对其纠正。纠正前(-1) %2 = (-1),纠正之后 ( (-1) % 2 + 2) % 2=1 保存在哈希表中的则为 1.则不会漏掉部分情况,例如输入为 [-1,2,9],K = 2如果不对其纠正则会漏掉区间 [2] 此时 2 % 2 = 0符合条件但是不会被计数。

那么这个题目我们可不可以用数组,代替 map 呢?当然也是可以的,因为此时我们的哈希表存的是余数,余数最大也只不过是 K-1所以我们可以用固定长度 K 的数组来模拟哈希表。

class Solution {
    public int subarraysDivByK(int[] A, int K) {
        int[] map = new int[K];
        map[0] = 1;
        int len = A.length;
        int presum = 0;
        int count = 0;
        for (int i = 0; i < len; ++i) {
            presum += A[i];
            //求key
            int key = (presum % K + K) % K;
            //count添加次数并将当前的map[key]++;
            count += map[key]++;         
        }
        return count;
    }
}