前缀和:和可被K整除的子数组(normal)

本题经重构思考+深度研究各个方面总结。

目录

思路

结构

实现

前缀和

哈希值纳入

余数

非正式版本

调试验证:

bug

深度分析此bug

最后版本


题目链接在这

元素限制:

  • -104 <= nums[i] <= 104

求数组内连续元素和是K整数倍的子数组数目。

连续+和值

思路

思路一:滑动窗口

结合滑动窗口------同向双指针+单调性+不可回复性(l无法回退到初位置0判断)。

由于存在负数导致单调性丧失,本题每次r移动后,l都要进行从最初位置到r位置元素和的分别判断->O(N^2)。

滑动窗口+哈希表

哈希表存储和值->前缀和思想

思路二:前缀和

向后查找(下标小的方向)特定值的**个数(不是找到而是个数)**来弥补字段和距离整除的差距值。

☆☆☆具体实现规则:

我们发现若某一段连续数组元素和想要被K整除,那么在其前缀和值fdpi 的基础上必须在fdp先前下标1,i-1中找到目标fdpn出现的次数(1 <= n <= i-1),就是寻找首余数fdpi%K和其副余数(12%7的副余数-2,-3%7的副余数4),而因此每一段数组和的哈希表元素的纳入方式是hashdp\[i%k]++。

结构

实现

前缀和

cpp 复制代码
vector<int> fdp(nums.size()+1,0);
for(int i = 1;i <= nums.size();i++)
{
     fdp[i] = fdp[i-1]+nums[i-1];
     //printf("%d ",fdp[i]); 验证日志
}

哈希值纳入

cpp 复制代码
hash[fdp[i]%k]++;

余数

首余数与副余数

cpp 复制代码
//首余数
fdp[i]%k;
//副余数
int t = fdp[i]%k > 0? fdp[i]%k-k : fdp[i]%k+k;

非正式版本

cpp 复制代码
void test1(vector<int>& nums, int k)
{
    vector<int> fdp(nums.size() + 1, 0);
    for (int i = 1; i <= nums.size(); i++)
    {
        fdp[i] = fdp[i - 1] + nums[i - 1];
        //printf("fdp[i]:%d ", fdp[i]);
    }
    int count = 0;

    //哈希映射查找
    unordered_map<int, int> hash;

    for (int i = 1; i <= nums.size(); i++)
    {
        hash[fdp[i] % k]++;
        //首余数处理
        count += hash[fdp[i] % k];
        //副余数处理
        int t = fdp[i] % k > 0 ? fdp[i] % k - k : fdp[i] % k + k;
        count += hash[t];
    }
    cout << count;
}

调试验证:

走读发现:

首余数处理的部分,每次都会将刚刚纳入的值hashdp\[i]加上作为首余数,但我们找的是0,i-1间的目标首余数,因此对hash值"-1"忽略本次进循环时的++造成的误差。

bash 复制代码
count += hash[fdp[i]%k]-1;

问:既然首余数出现这种问题,那么副余数会不会呢?

如果会的话:

①fdpi % k == fdpi % k - k --->k == 0

②fdpi % k == fdpi % k + k --->k==0

以上两者均不会出现因为:

①题目" 2 <= k <= 10^4"

②任何数%0 无意义报错!

bug

当为k的整数倍的numsi-1第一次出现,count主余数应该使得count+=1;但因为"-1"导致加的是"0"。 // 丢失值使得数据比预期小

深度分析此bug

问:若后续继续出现numsi-1的整数倍会怎么样?

可以预见的是由于第一次落下"1"hash0没有加上,以后再次出现就会"永远比预期差一步"。 // 1 2 3 4 ->每次出现分别会差一次的值。

解决办法:

在哈希初始化时就对hash0++;

差值形成具体体现如下:

|---|-----------|-------|---|---|-----------|-------|
| i | hash0 | count | | i | hash0 | count |
| 2 | 1 | 0 | | 2 | 2 | 1 |
| 3 | 2 | 1 | | 3 | 3 | 3 |
| 4 | 3 | 3 | | 4 | 4 | 6 |

最后版本

cpp 复制代码
void test1(vector<int>& nums, int k)
{
    vector<int> fdp(nums.size() + 1, 0);
    for (int i = 1; i <= nums.size(); i++)
    {
        fdp[i] = fdp[i - 1] + nums[i - 1];
        //printf("fdp[i]:%d ", fdp[i]);
    }
    int count = 0;
    //哈希映射查找
    unordered_map<int, int> hash;
    hash[0]++;
    for (int i = 1; i <= nums.size(); i++)
    {
        hash[fdp[i] % k]++;
        //首余数处理
        count += hash[fdp[i] % k] - 1;
        //副余数处理
        int t = fdp[i] % k > 0 ? fdp[i] % k - k : fdp[i] % k + k;
        count += hash[t];
    }
    cout << count;
}

写给我自己的话:算法是当下(Linux IO中途刚到)学习的重点,价值在于严谨思维逻辑的锻炼、也是当下阶段提高思维方法有效武器。该算法题目纯手搓+额外问题的自行研究弱化学习内容面的扩大的作用,这种拓展研究能力调试能力更是真正价值的体现,坚持与调整算法题目的解答不仅作用于解题,更作用于我简介的"打破固有陋习"调整"学习方法"。(2026-0523)

相关推荐
devilnumber25 分钟前
Java 递归算法 详解 + 核心要点 + 实战运用 + 避坑指南
java·开发语言·算法
unicrom_深圳市由你创科技1 小时前
哪些控制逻辑应该放在 PLC,哪些放在上位机?
c++
‎ദ്ദിᵔ.˛.ᵔ₎2 小时前
双指针、滑动窗口、前缀和、二分查找 算法
算法
顾北顾3 小时前
多头注意力机制
人工智能·深度学习·算法
H178535090963 小时前
SolidWorks_基于草图的实体特征20_特征错误排查
算法·3d建模·solidworks
hujinyuan201603 小时前
2025年12月中国电子学会青少年机器人技术等级考试试卷(二级) 真题+答案
人工智能·算法·机器人
玖玥拾3 小时前
C/C++ 基础笔记(十三)继承
c语言·c++·继承
bIo7lyA8v4 小时前
算法复杂度评估的实验统计方法与可视化的技术8
算法
李老师讲编程4 小时前
中国电子学会图形化2020.12月Scratch三级考级题
算法·scratch·信息学奥赛·图形化编程·scratch素材
ao-weilai4 小时前
C++:哈希表
c++·哈希算法·散列表