LeetCode C++ 1590. Make Sum Divisible by P【数学/前缀和】中等
发布日期:2021-07-01 02:52:05
浏览次数:2
分类:技术文章
本文共 4219 字,大约阅读时间需要 14 分钟。
Given an array of positive integers nums
, remove the smallest subarray (possibly empty) such that the sum of the remaining elements is divisible by p
. It is not allowed to remove the whole array.
Return the length of the smallest subarray that you need to remove, or -1 if it’s impossible.
A subarray is defined as a contiguous block of elements in the array.
Example 1:
Input: nums = [3,1,4,2], p = 6Output: 1Explanation: The sum of the elements in nums is 10, which is not divisible by 6. We can remove the subarray [4], and the sum of the remaining elements is 6, which is divisible by 6.
Example 2:
Input: nums = [6,3,5,2], p = 9Output: 2Explanation: We cannot remove a single element to get a sum divisible by 9. The best way is to remove the subarray [5,2], leaving us with [6,3] with sum 9.
Example 3:
Input: nums = [1,2,3], p = 3Output: 0Explanation: Here the sum is 6. which is already divisible by 3. Thus we do not need to remove anything.
Example 4:
Input: nums = [1,2,3], p = 7Output: -1Explanation: There is no way to remove a subarray in order to get a sum divisible by 7.
Example 5:
Input: nums = [1000000000,1000000000,1000000000], p = 3Output: 0
Constraints:
1 <= nums.length <= 105
1 <= nums[i] <= 109
1 <= p <= 109
题意:给出一个正整数数组 nums
,移除 最短子数组(可以为空),使得剩余元素的和能被 p
整除。 不允许将整个数组都移除。返回这个最短子数组的长度。
思路:这一题和模运算有关系,可以使用前缀和进行算法优化。整个算法的步骤如下:
- 计算整个数组的和 s u m {sum} sum ,如果 sum m o d p == 0 \text{sum}\bmod \text{p == 0} summodp == 0 ,就不需要移出任何子数组,直接返回 0 0 0 ;
- 设 x = sum m o d p \text{x = sum} \bmod \text{p} x = summodp ,如果我们要移除子数组,使得剩余元素的和能够被 p p p 整除,或者说是 p p p 的整数倍(倍数设为 k k k );
- 根据同余的定义可知,子数组的和 s u b s u m subsum subsum 应与整个数组的和 s u m sum sum 模 p p p 同余,即: subsum ≡ sum ( m o d p ) \text{subsum} \equiv \text{sum}\pmod{\text{p}} subsum≡sum(modp)
- 或者由模运算对减法的封闭性: (sum m o d p - subsum m o d p) m o d p = ( sum - subsum) m o d p = 0 \text{(sum} \bmod \text{p - subsum} \bmod \text{p)} \bmod \text{p} =(\text{sum - subsum)} \bmod \text{p} = 0 (summodp - subsummodp)modp=(sum - subsum)modp=0 可知, s u m sum sum 和 s u b s u m subsum subsum 模 p p p 的余数需要相等。
- 于是,问题转换为: 怎样判断有没有这样的子数组,如何找到模 p p p 的余数等于 x x x 的子数组?似乎做过类似的题目:
523. Continuous Subarray Sum
——只不过那道题是寻找有没有模 k k k 的余数为 0 0 0 的子数组。拓展一下,假设现在手中有一段前缀和 s u m j sum_j sumj ,它模 p p p 的余数为 q q q ,要寻找有没有模 p p p 的余数等于 x x x 的子数组 a r r i , j arr_{i,j} arri,j ,就只需要判断是否存在一个前缀和 s u m i − 1 sum_{i-1} sumi−1 ,它模 p p p 的值等于 ( q − x ) m o d p (q - x)\ \bmod\ p (q−x) mod p 。 - 于是,我们只需要用映射,在其中寻找有没有前缀和模 p p p 的余数,等于当前的前缀和 s u m j sum_j sumj 模 p p p 的余数 q q q 减去 x x x :
- 如果没有找到,就记录当前位置前缀和 % p \%\ p % p 的值及其最右位置;
- 如果找到,且长度更短,就更新最短的子数组长度。
以 nums = [3,1,4,2], p = 6
为例:
前缀和 % p 的序列 = [3,4,2,4]整个数组的和 %6 等于 4--------------------------------------------------------------------------sumj = 4, sumj % 6 = 4 时, 要判断有没有某个子数组模 6 等于 4只需要判断有没有某个前缀和模 6 等于 0.最初的前缀和等于 0, 所以此时移除子数组 nums[0:2) , 长度为2.--------------------------------------------------------------------------sumj = 8, sumj % 6 = 2 时, 要判断有没有某个子数组模 6 等于 4只需要判断有没有某个前缀和模 6 等于 (2 - 4) % 6 = 4可知sumi = 3 + 1 = 4, sumi % 6 = 4, 于是移除子数组 nums[2], 长度为1, 符合题意
整个代码如下:
class Solution { public: int minSubarray(vector & nums, int p) { int sum = 0, n = nums.size(); for (int i = 0; i < n; ++i) sum = (sum + nums[i]) % p; if (sum % p == 0) return 0; //整个正整数数组的和已经可以被p整除 int ansLen = -1, now = 0; //最短子数组的长度 unordered_maprecord; record[0] = 0; //如果sum % p == x, 那么移除的子数组之和t应该满足t % p == x //(sum % p - t % p) % p = (sum - t) % p = 0 for (int i = 0; i < n; ++i) { now = (now + nums[i]) % p; //此处前缀和 % p == now //如果有一个前缀和 %p == (now - x) % p, 那么存在子数组 %p == x int last = (now - sum + p) % p; //+p,防止now-sum为负数,出现取余和取模的不一致 unordered_map ::iterator it = record.find(last); if (it != record.end()) if (ansLen == -1 || i + 1 - it->second < ansLen) ansLen = i + 1 - it->second; //更新最短子数组的长度 record[now] = i + 1; //记录前缀和和这一前缀和的数组长度 } if (ansLen == n) return -1; //不可以移除整个数组 return ansLen; }};
提交结果如下:
执行用时:420 ms, 在所有 C++ 提交中击败了70.96% 的用户内存消耗:65.2 MB, 在所有 C++ 提交中击败了91.90% 的用户
转载地址:https://memcpy0.blog.csdn.net/article/details/108740518 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
初次前来,多多关照!
[***.217.46.12]2024年04月23日 15时40分58秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
linux查看硬件信息
2019-05-02
linux支持大于4G内存
2019-05-02
WM_GETINFO相关
2019-05-02
填入空隙(setbkcolor,setbkmode)
2019-05-02
[收藏] FC交换机基础知识详解
2019-05-02
NVMe技术架构深度分析
2019-05-02
技术爆炸时代如何做技术的掌控者?
2019-05-02
机柜服务器如何选择,有哪些学问?
2019-05-02
Ceph存储系统Scrub机制分析
2019-05-02
OpenStack重组,敢问未来路在何方?
2019-05-02
CTO,是怎样炼成的?
2019-05-02
选择GPU服务器的基本原则
2019-05-02
关于数据中台系统,需要了解哪些技术?
2019-05-02
全面分析HDFS基本技术原理
2019-05-02
详解以太网介质技术发展史!
2019-05-02
详解“硬核”虚拟化技术SR-IOV原理
2019-05-02
SAP HANA解决方案设计10问详解
2019-05-02
详解内存运算架构、挑战和趋势
2019-05-02
Lightbits能否让NVMe/TCP新标准旗开得胜?
2019-05-02
数据中台,何为正解?!
2019-05-02