可被K整除的子数组问题 | 豆包MarsCode AI 刷题

问题描述

小F正在研究数组的子数组问题。给定一个整数数组 nums 和一个整数 k,你需要找到数组中所有元素之和可以被 k 整除的非空子数组的数目。注意,子数组是数组中的连续部分。


测试样例

样例1:

输入:nums = [4,5,0,-2,-3,1] ,k = 5

输出:7

样例2:

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

输出:3

样例3:

输入:nums = [-1,2,9] ,k = 2

输出:2

思路分析

  1. 前缀和的概念

    • 前缀和是指从数组的开头到某个位置的所有元素之和。例如,对于数组 [a1, a2, a3, ..., an],其前缀和数组为 [s1, s2, s3, ..., sn],其中 si = a1 + a2 + ... + ai
  2. 利用前缀和求解

    • 如果两个前缀和 s1s2 满足 (s1 - s2) % k == 0,那么从 s2+1s1 的子数组之和就能被 k 整除。
    • 因此,我们可以使用前缀和来解决这个问题。具体来说,我们需要记录每个前缀和对 k 取模的结果,并统计这些结果的出现次数。
  3. 处理负数情况

    • 在 JavaScript 中,模运算可能会产生负数结果。为了避免这种情况,如果模运算结果为负数,我们需要将其调整为正数。

具体步骤

  1. 初始化前缀和 prefixSum 为 0,并创建一个哈希表 prefixSumModK 来存储前缀和对 k 取模的结果及其出现次数。初始时,将 prefixSumModK.set(0, 1) 设置为 1,表示前缀和为 0 的情况已经存在一次。
  2. 遍历数组 nums,计算当前前缀和 prefixSum
  3. 计算当前前缀和对 k 取模的结果 modulus,并确保其为非负数(即 modulus < 0 时,modulus += k)。
  4. 更新计数 count,加上 prefixSumModK.get(modulus) 的值(如果没有出现过,则取默认值 0)。
  5. 更新 prefixSumModKmodulus 的计数。
  6. 返回最终的计数 count

代码详解

ini 复制代码
function solution(nums, k) {
    let count = 0;
    let prefixSum = 0;
    let prefixSumModK = new Map();  // 初始化前缀和对 k 取模的哈希表,包含 0 的位置为 1
    prefixSumModK.set(0, 1);  // 初始化时设置 0 的位置为 1

    for (let num of nums) {
        prefixSum += num;
        let modulus = prefixSum % k;
        if (modulus < 0) modulus += k;  // 处理负数情况,确保模运算结果为非负数
        count += (prefixSumModK.get(modulus) || 0);  // 以当前位置为结束位置的子数组和可以被 k 整除的数量
        prefixSumModK.set(modulus, (prefixSumModK.get(modulus) || 0) + 1);  // 更新前缀和对 k 取模的值
    }

    return count;
}

// 测试代码
console.log(solution([4, 5, 0, -2, -3, 1], 5) === 7);
console.log(solution([1, 2, 3], 3) === 3);
console.log(solution([-1, 2, 9], 2) === 2);

涉及到的知识点

1. 前缀和
  • 定义:前缀和是指从数组的开头到某个位置的所有元素之和。
  • 用途 :通过前缀和可以快速计算任意子数组的和。如果两个前缀和的差值能被 k 整除,那么这两者的子数组之和也能被 k 整除。
2. 模运算
  • 定义:模运算是一种取余运算,通常用于确定一个数除以另一个数的余数。
  • 处理负数 :在某些编程语言中,模运算可能会产生负数结果。为了保证模运算结果为非负数,需要进行适当的调整(如 if (modulus < 0) modulus += k)。
3. 哈希表(Map)
  • 定义:哈希表是一种数据结构,可以在常数时间内完成插入、删除和查找操作。
  • 用途 :本题中使用哈希表来记录前缀和对 k 取模的结果及其出现次数,从而高效地统计满足条件的子数组数量。
4. 子数组
  • 定义:子数组是原数组中的连续部分。
  • 求解方法:通过前缀和的方法,可以快速找到所有符合条件的子数组。
5. 计数与统计
  • 目的:统计满足条件的子数组数量。
  • 实现方式:在遍历数组的过程中,通过更新哈希表中的计数来统计符合条件的子数组。

总结

通过青训营豆包MarsCode AI刷题,我学到了以下几点:

  1. 前缀和的概念及应用

    • 前缀和可以帮助我们快速计算任意子数组的和。这对于解决一些涉及子数组和的问题非常有用。
  2. 模运算的理解

    • 模运算是一种取余运算,用于确定一个数除以另一个数的余数。理解如何处理模运算中的负数结果对于编程非常重要。
  3. 哈希表的应用

    • 哈希表是一种高效的键值对存储结构,适用于频繁的插入、删除和查找操作。在这类问题中,哈希表可以帮助我们快速统计符合条件的子数组数量。
  4. 子数组的统计方法

    • 通过前缀和和哈希表的结合使用,可以有效地统计出满足特定条件的子数组数量。这种方法不仅效率高,而且易于理解和实现。
  5. 算法优化思路

    • 这类问题展示了如何将多个概念(前缀和、模运算、哈希表)结合起来,达到优化算法的目的。理解这种组合思维有助于解决更多复杂问题。 总而言之,通过刷题我不仅学会了如何应用前缀和和哈希表,还了解了如何处理模运算中的边界条件和负数情况。这些技能在处理其他类似的数组问题时也非常有用。
相关推荐
静水流深3971 个月前
03 模型IO| 豆包MarsCode AI刷题
青训营笔记
用户247841860021 个月前
第七次算法笔记 | 豆包MarsCode AI刷题
青训营笔记
幻61 个月前
小S的倒排索引 | 豆包MarsCode AI刷题
青训营笔记
用户826014428301 个月前
469. 环形数组最大子数组和问题
青训营笔记
用户605721920981 个月前
奇妙货币交易问题 | 豆包MarsCode AI刷题
青训营笔记
我明天再来学Web渗透2 个月前
“抖音互联网架构分析及高可用系统构建思考”(方向三)| 豆包MarsCode AI刷题
青训营笔记
用户302133066202 个月前
第三次刷题 | 豆包MarsCode AI刷题
青训营笔记
用户9105973027702 个月前
CSS详解| 豆包MarsCode AI刷题
青训营笔记
huyck2 个月前
伴学笔记1|豆包MarsCode AI 刷题
青训营笔记