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

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

目录

思路

结构

实现

前缀和

哈希值纳入

余数

非正式版本

调试验证:

bug

深度分析此bug

最后版本


题目链接在这

元素限制:

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

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

连续+和值

思路

思路一:滑动窗口

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

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

滑动窗口+哈希表

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

思路二:前缀和

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

☆☆☆具体实现规则:

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

调试验证:

走读发现:

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

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

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

如果会的话:

①fdp[i] % k == fdp[i] % k - k --->k == 0

②fdp[i] % k == fdp[i] % k + k --->k==0

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

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

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

bug

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

深度分析此bug

问:若后续继续出现nums[i-1]的整数倍会怎么样?

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

解决办法:

在哈希初始化时就对hash[0]++;

差值形成具体体现如下:

|---|-----------|-------|---|---|-----------|-------|
| i | hash[0] | count | | i | hash[0] | 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)

相关推荐
艾莉丝努力练剑11 小时前
【Linux网络】Linux 网络编程:HTTP(五)HTTP收尾,从Cookie会话保持、抓包问题到 HTTPS 初识
linux·运维·服务器·网络·c++
世纪末的小黑12 小时前
【LeetCode自用】LeetCode自用记录贴,题目一:两数之和
数据结构·算法·leetcode
兰令水12 小时前
topcode【随机算法题】【2026.5.22打卡-java版本】
java·算法·leetcode
努力努力再努力wz12 小时前
【Redis入门系列】:Redis 内部编码机制与 String 深度解析:SDS 底层实现、三种编码与核心命令详解
c语言·开发语言·数据结构·数据库·c++·redis·缓存
Brilliantwxx12 小时前
【C++】 认识STL set与map(基础接口+题目OJ运用)
开发语言·数据结构·c++·笔记·算法
05候补工程师12 小时前
【线性代数】核心考点复习笔记:二次型配方法、施密特正交化步骤与特征值经典题型详解
经验分享·笔记·线性代数·考研·算法
Huangjin007_12 小时前
【C++ STL篇(十一)】深入浅出红黑树:从原理到实现,一篇搞定
开发语言·c++
fqbqrr12 小时前
2605C++,C++继承类实现调试器
开发语言·c++
Deep-w12 小时前
【MATLAB】基于遗传算法的直流电机 PI 控制器参数优化研究
开发语言·算法·matlab