前缀和:和可被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 分钟前
lambda表达式的使用(3)
开发语言·c++·lambda
2501_9065651219 分钟前
数学之美探究
算法
oier_Asad.Chen29 分钟前
【洛谷题解/AcWing题解/OI学习笔记】洛谷P2868【USACO07DEC】Sightseeing Cows G(01分数规划求最大比率)
算法·图论·spfa·二分·负环
C++ 老炮儿的技术栈2 小时前
基于Qt实现轻量化本地音乐播放器
开发语言·c++·qt·c·播放器·音乐
leisoo80972 小时前
财报数据怎么排雷本地化Python构建财务异常预警系统
人工智能·python·算法
qz_Serene4 小时前
C++:类和对象(上)
开发语言·c++
luj_17684 小时前
塔防牌:策略与卡牌的智慧碰撞
服务器·c语言·开发语言·经验分享·算法
wuyk5555 小时前
3.链表:用指针串联的动态数据结构
c语言·开发语言·数据结构·链表
郝学胜-神的一滴5 小时前
干货版《算法导论》17:二叉树核心原理、遍历逻辑与高阶实操全解
数据结构·c++·python·算法·计算机·编程
爱编程的小新☆5 小时前
【LeetCode】从递归到 Flood Fill:5 道题吃透 DFS 的选择、回溯与标记
java·算法·leetcode·深度优先·回溯·flood fill