前缀和:和可被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)

相关推荐
倒头就睡的小比特3 天前
算法竞赛C++常用的STL
c++·算法
weilx12343 天前
C++笔记-文件IO-<fcntl.h>
c++
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼3 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
Smileyqp沛沛3 天前
前端?C++ ?较大差异基础罗列
c++·基础·前端转c++
m0_547486663 天前
《数据结构教程》全套 PPT课件2026
数据结构
C语言小火车3 天前
C/C++ 为什么需要编译器?
开发语言·c++
旖旎夜光3 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark3 天前
大规模并行计算中的负载均衡算法研究4
算法
吞下星星的少年·-·3 天前
C++ 萌新语法入门篇
c++·算法比赛