[LeetCode] 1590. Make Sum Divisible by P

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 = 6
Output: 1
Explanation: 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 = 9
Output: 2
Explanation: 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 = 3
Output: 0
Explanation: Here the sum is 6. which is already divisible by 3. Thus we do not need to remove anything.

Constraints:
1 <= nums.length <= 105
1 <= nums[i] <= 109
1 <= p <= 109

使数组和能被 P 整除。

给你一个正整数数组 nums,请你移除 最短 子数组(可以为 空),使得剩余元素的 和 能被 p 整除。 不允许 将整个数组都移除。

请你返回你需要移除的最短子数组的长度,如果无法满足题目要求,返回 -1 。

子数组 定义为原数组中连续的一组元素。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/make-sum-divisible-by-p
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

思路是前缀和。这算是前缀和类型的题目里比较麻烦的一道题,思路不难看出来但是里面有一些细节的实现需要注意。

题目问的是能否找到一个最短的子数组满足移除这个子数组之后,剩下的部分能被 P 整除。一个容易想到的 corner case 是如果整个数组的前缀和就能被 P 整除,那么直接返回 0 即可。

一般的 case 是,假设整个数组的前缀和 = sum,sum % p = remainder。我们需要找的是一个最短的子数组,他的和 sum2 = remainder,这样 sum - sum2 就能被 P 整除了。如下图,我们找一个最短的黄色的部分,然后黄色的部分 % P = remainder 即可。

[x, x, x, x, ==x, x, x, x, x==, x, x, x, x, x, x, x]

注意我们在累加前缀和的时候,每次加一个数字,都要 % P,这样使得 sum2 总是落在 [0, p) 这个范围内。包括代码内怎么找 j 的方法,需要确保模运算的两个数字都是正数。这是模运算的一些常用技巧。

复杂度

时间O(n)
空间O(n)

代码

Java实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public int minSubarray(int[] nums, int p) {
long sum = 0;
for (int num : nums) {
sum += num;
}
// corner case
if (sum % p == 0) {
return 0;
}

// normal case
int remainder = (int) (sum % p);
int n = nums.length;
int res = n;
HashMap<Integer, Integer> map = new HashMap<>();
map.put(0, -1);
int sum2 = 0;
for (int i = 0; i < n; i++) {
sum2 = (sum2 + nums[i]) % p;
map.put(sum2, i);
int target = (sum2 - remainder + p) % p;
int j = map.getOrDefault(target, -n);
res = Math.min(res, i - j);
}
return res < n ? res : -1;
}
}

[LeetCode] 1590. Make Sum Divisible by P
https://shurui91.github.io/posts/1110332484.html
Author
Aaron Liu
Posted on
March 10, 2023
Licensed under