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

  1. 974、和可被K整除的子数组
    1. 示例:
  2. 题解
    1. 1、前缀和
      1. 暴力枚举——超时
      2. 1.1、哈希表 + 逐一统计
      3. 1.2、哈希表 + 单次统计

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]

提示:

  • 1 <= A.length <= 30000
  • -10000 <= A[i] <= 10000
  • 2 <= K <= 10000

链接:https://leetcode-cn.com/problems/subarray-sums-divisible-by-k

题解

1、前缀和

计算数组的前缀和,对于每一位的前缀和,先判断是否可以整除K,然后从左侧依次减去元素,判断是否可以被整除。

暴力枚举——超时

class Solution {
    public int subarraysDivByK(int[] A, int K) {
        int[] accA = new int[A.length];
        int count = 0;
        for (int i = 0;i < A.length;i++) {
            accA[i] += A[i];
            if (i > 0) {
                accA[i] += accA[i - 1];
            }
        }

        for (int i = 0;i < A.length;i++) {
            // 0~i [0,i]
            if (accA[i]%K == 0) {
                count++;
            }
            // j~i ==> (j,i]
            for (int j = 0;j < i;j++) {
                if ((accA[i] - accA[j]) % K == 0) {
                    count++;
                }
            }
        }

        return count;
    }
}

1.1、哈希表 + 逐一统计

$$
(P[j] - P[i - 1]) % K = 0 \

可变形为:P[j] % K = P[i - 1] % K
$$
这样,可以统计每一个位置的前缀和,前缀和相同的两个位置对应的连续数组一定可以被K整除

class Solution { 
    public int subarrayDivByK(int[] A,int K) {
        Map<Integer,Integer> record = new HashMap<>();
        // 前缀和本身可以被K整除
        record.put(0, 1);
        int sum = 0,ans = 0;
        for (int a : A) {
            sum += a;
            // 将取模结果变为正数,+K不影响结果
            int mod = (sum % K + K) % K;
            // 获取mod相同的出现次数
            int same = record.getOrDefault(mod, 0);
            ans += same;
            // 累加统计次数
            record.put(mod,same + 1);
        }
        return ans;
    }
}
  • 直接用数组作为hash表
class Solution {
    public int subarraysDivByK(int[] A, int k) {
        int[] m = new int[k];
        m[0] = 1;
        int count = 0,sum = 0;
        for (int a: A ) {
            sum = (sum + a ) % k;
            if(sum < 0) {
                sum += k;
            }
            count += m[sum];
            m[sum]++;
        }
        return count;
    }
}

1.2、哈希表 + 单次统计

考虑方法一中的思路,我们可以在遍历只维护哈希表。在遍历结束后,我们再遍历哈希表,用排列组合的方法来统计答案。

class Solution {
    public int subarraysDivByK(int[] A, int K) {
        Map<Integer, Integer> record = new HashMap<>();
        record.put(0, 1);
        int sum = 0;
        for (int elem: A) {
            sum += elem;
            // 注意 Java 取模的特殊性,当被除数为负数时取模结果为负数,需要纠正
            int modulus = (sum % K + K) % K;
            record.put(modulus, record.getOrDefault(modulus, 0) + 1);
        }

        // 进行排列组合计算,两两握手
        int ans = 0;
        for (Map.Entry<Integer, Integer> entry: record.entrySet()) {
            ans += entry.getValue() * (entry.getValue() - 1) / 2;
        }
        return ans;
    }
}

// 作者:LeetCode-Solution
// 链接:https://leetcode-cn.com/problems/subarray-sums-divisible-by-k/solution/he-ke-bei-k-zheng-chu-de-zi-shu-zu-by-leetcode-sol/
  • 优雅实现
class Solution {
    public int subarraysDivByK(int[] A, int K) {
        // 计算第一个的余数
        A[0] = A[0] % K;
        // 统计前缀和的余数
        for (int i = 1;i < A.length;i++) A[i] = (A[i-1] + A[i]) % K;
        // 统计余数相同的个数
        int[] count = new int[K];
        count[0]++;
        for (int x : A) count[(x % K + K) % K]++;
        // 计算一共有多少个
        int ans = 0;
        for (int y : count) ans = ans + y * (y - 1) / 2;
        return ans;
    }
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1056615746@qq.com

💰

Title:974、和可被K整除的子数组

Count:827

Author:攀登

Created At:2020-07-26, 00:19:44

Updated At:2024-06-15, 15:52:32

Url:http://jiafeimao-gjf.github.io/2020/07/26/974%E3%80%81%E5%92%8C%E5%8F%AF%E8%A2%ABK%E6%95%B4%E9%99%A4%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84/

Copyright: 'Attribution-non-commercial-shared in the same way 4.0' Reprint please keep the original link and author.

×

Help us with donation