leetcode
leetcode 851 ~ 900
和可被 K 整除的子数组

和可被 K 整除的子数组

难度:

标签:

题目描述

代码结果

运行时间: 48 ms, 内存: 19.1 MB


/*
 * 思路:
 * 1. 使用前缀和数组来计算连续子数组的和。
 * 2. 利用 HashMap 来存储当前前缀和 mod k 的余数及其出现次数。
 * 3. 如果当前前缀和的 mod k 余数在 HashMap 中已经存在,说明从上一次出现该余数的位置到当前的位置之间的子数组之和可被 k 整除。
 * 4. 使用 Java Stream API 遍历数组,更新 HashMap 和结果计数。
 */

import java.util.HashMap;
import java.util.stream.IntStream;

public class Solution {
    public int subarraysDivByK(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(0, 1);
        int[] count = {0};
        int[] prefixSum = {0};
        IntStream.of(nums).forEach(num -> {
            prefixSum[0] += num;
            int mod = (prefixSum[0] % k + k) % k; // 确保 mod 为正值
            count[0] += map.getOrDefault(mod, 0);
            map.put(mod, map.getOrDefault(mod, 0) + 1);
        });
        return count[0];
    }
}

解释

方法:

此题解采用了前缀和与哈希表的结合思想。首先,遍历数组,计算每个位置的前缀和,并对k取余,得到余数。使用哈希表record记录每个余数出现的次数。由于子数组的和可以表示为两个前缀和的差,如果两个前缀和对k取余相同,那么它们的差也能被k整除。因此,对于每个余数,从对应的次数中选取两个不同的前缀和(即组合数C(n,2)),累加到结果中。

时间复杂度:

O(n)

空间复杂度:

O(k)

代码细节讲解

🦆
在解法中,哈希表的键值对中,键代表什么意义?为什么选择这个作为键?
在解法中,哈希表的键表示数组中前缀和对整数k取余后得到的余数。选择余数作为键是因为根据数学性质,如果两个数的差能够被k整除,那么这两个数对k取余的结果必定相同。因此,通过记录每个余数出现的次数,可以快速计算出有多少对前缀和的差能被k整除。
🦆
为什么在哈希表`record`初始化时要设置{0:1},这里的1代表了什么意义?
在哈希表`record`中初始化{0:1}是为了处理那些从数组起始位置直到某一点的子数组和本身就是k的倍数的情况。这里的1代表数组的和从开始到当前位置(包括整个数组)取余k为0的情况已经出现1次。这样做是为了确保可以统计包括数组首元素在内的所有符合条件的子数组。
🦆
在哈希表更新`record[m] = record.get(m, 0) + 1`这一步中,为什么选择对已有的值进行累加,而不是直接赋值?
在更新哈希表时选择对已有的值进行累加是因为我们需要记录每个余数出现的总次数。每次计算得到一个新的前缀和的余数时,我们需要增加该余数出现的计数,以便后续可以计算出从数组中任意位置到另一位置形成的子数组和是否能被k整除。直接赋值会丢失之前该余数出现的频次信息。
🦆
题解中提到的组合数公式`v * (v-1) // 2`是如何得出的,它在这个算法中起什么作用?
组合数公式`v * (v-1) // 2`用于计算从v个相同余数中选择两个(不同的前缀和位置)以形成子数组的方法数,这是基于组合数学中的组合公式C(n, 2) = n(n-1)/2。在算法中,这个公式用来计算每一种余数对应的所有可能的前缀和对的数量,这些对的差能够被k整除。因此通过该公式可以直接得到满足条件的子数组的数量。

相关问题

和为 K 的子数组

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 

子数组是数组中元素的连续非空序列。

 

示例 1:

输入:nums = [1,1,1], k = 2
输出:2

示例 2:

输入:nums = [1,2,3], k = 3
输出:2

 

提示:

  • 1 <= nums.length <= 2 * 104
  • -1000 <= nums[i] <= 1000
  • -107 <= k <= 107