leetcode974. 和可被 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

思路

思路一(暴力解法)

       遍历所有的子数组,判断每个子数组的和是否可以整除K.时间复杂度太高。

思路二(前缀和+哈希表+边遍历边计算)

       求子数组的问题应该常考虑前缀和的解法。

       前缀和即为前n个元素的和。例如用p(n) = p(0) + p(1) + … + p(n).所以子数组[i,j]的和就可以表示为p(j) – p(i – 1).

       判断子数组[i,j]的和是否可以整除k,即判断(p(j) – p(i – 1) % k == 0是否成立,有因为同余定理。判断p(j) % k == p(i – 1) % K是否成立。

       所以我们可以用hashmap记录前缀和模k的余数出现的次数,即用余数做key,它对应出现的次数为value,每次记录到p(j) % k的时候,判断此时hashmap中键为p(j) % k的值是多少。

思路三(前缀和+哈希表+排列组合)

       上述的思路是在遍历计算前缀和时候就计算个数,我们可以一次性给哈希表赋完值之后用排列组合的方式计算个数。

       例如余数为1的个数有n个,则n个里面可以两两配对,即余数为1的有n*(n – 1) / 2个子数组,所以对hashmap遍历,判断每种余数的子数组个数即可。

代码

思路二代码

public static int subarraysDivByK(int[] A, int K) {
    Map<Integer,Integer> record = new HashMap<>();
    record.put(0,1);
    int sum = 0,ans = 0;
    for(int ele : A){
        sum += ele;
        int moudle = (sum % K + K) % K;
        int sam = record.getOrDefault(moudle,0);
        ans += sam;
        record.put(moudle,sam + 1);
    }
    return ans;
}

思路三代码

public static int subarraysDivByK(int[] A, int K) {

    Map<Integer,Integer> record = new HashMap<>();

    record.put(0,1);

    int sum = 0,ans = 0;

    for(int ele : A){

        sum += ele;

        int moudle = (sum % K + K) % K;

        int sam = record.getOrDefault(moudle,0);

        record.put(moudle,sam + 1);

    }

    for(Map.Entry<Integer,Integer> entry : record.entrySet()){

        ans += (entry.getValue() * (entry.getValue() - 1)) / 2;

    }

    return ans;

}

 

总结

暴力算法的时间复杂度为O(n^2),不满足题目要求。

思路二的时间复杂度为O(n),空间复杂度为O(min(N,K))。

思路三的时间复杂度为O(n),空间复杂度为O(min(N,K))。

求子数组的问题多想想前缀和,将O(n^2)时间复杂度降到O(n).

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值