Given an array of positive integers nums
and a positive integer p
, your task is to remove the smallest possible non-empty subarray (i.e., a contiguous sequence of elements) from nums
such that the sum of the remaining elements is divisible by p
. You cannot remove the entire array. If it is not possible to make the sum divisible by p
by removing any subarray, return -1
.
nums
is a positive integer.-1
if it's impossible.
At first glance, this problem might seem to require checking all possible contiguous subarrays and testing if removing them makes the sum of the rest divisible by p
. However, this brute-force approach would be far too slow for large arrays.
The key insight is to reframe the problem: Instead of focusing on what to remove, focus on what remains. If removing a subarray makes the sum of the rest divisible by p
, then the sum of the removed subarray must have a remainder equal to the total sum modulo p
. This observation allows us to use prefix sums and hash maps to efficiently find the shortest subarray whose sum has the required remainder.
We move from a brute-force, nested loop approach to a more optimal algorithm based on prefix sums and modular arithmetic.
Let's break down the optimized algorithm step-by-step:
total = sum(nums)
.total % p == 0
, then the sum is already divisible by p
, so we don't need to remove anything. But since removing nothing is not allowed, we return 0
only if a non-empty subarray can be removed; otherwise, proceed.rem = total % p
. We need to remove a subarray whose sum modulo p
is exactly rem
.p
at each index.p
equals rem
.p
.-1
.We use a hash map for constant time lookups of prefix sum remainders, which is crucial for efficiency.
Consider nums = [3, 1, 4, 2]
and p = 6
.
rem = 4
.
(current_mod - rem) % p
exists in the map.p
. This takes O(N2) time and O(1) space (not counting input).
p
. Each lookup and update is O(1), so the overall time complexity is O(N). The space complexity is O(p), since the hash map stores at most p
different remainders.
By reframing the problem in terms of modular arithmetic and prefix sums, we avoid brute-force enumeration and achieve significant speedup. The main insight is that removing a subarray with a specific sum remainder aligns with the desired divisibility of the total sum. Using a hash map for prefix sum remainders allows us to efficiently find the shortest subarray to remove, resulting in an elegant and optimal solution.
class Solution:
def minSubarray(self, nums, p):
total = sum(nums)
rem = total % p
if rem == 0:
return 0
prefix = 0
res = len(nums)
mods = {0: -1}
for i, num in enumerate(nums):
prefix = (prefix + num) % p
target = (prefix - rem) % p
if target in mods:
res = min(res, i - mods[target])
mods[prefix] = i
return res if res < len(nums) else -1
class Solution {
public:
int minSubarray(vector<int>& nums, int p) {
int n = nums.size();
long total = 0;
for (int num : nums) total += num;
int rem = total % p;
if (rem == 0) return 0;
unordered_map<int, int> mods;
mods[0] = -1;
int res = n;
long prefix = 0;
for (int i = 0; i < n; ++i) {
prefix = (prefix + nums[i]) % p;
int target = (prefix - rem + p) % p;
if (mods.count(target)) {
res = min(res, i - mods[target]);
}
mods[prefix] = i;
}
return res < n ? res : -1;
}
};
class Solution {
public int minSubarray(int[] nums, int p) {
long total = 0;
for (int num : nums) total += num;
int rem = (int)(total % p);
if (rem == 0) return 0;
Map<Integer, Integer> mods = new HashMap<>();
mods.put(0, -1);
int res = nums.length;
long prefix = 0;
for (int i = 0; i < nums.length; ++i) {
prefix = (prefix + nums[i]) % p;
int target = (int)((prefix - rem + p) % p);
if (mods.containsKey(target)) {
res = Math.min(res, i - mods.get(target));
}
mods.put((int)prefix, i);
}
return res < nums.length ? res : -1;
}
}
var minSubarray = function(nums, p) {
let total = nums.reduce((a, b) => a + b, 0);
let rem = total % p;
if (rem === 0) return 0;
let mods = new Map();
mods.set(0, -1);
let res = nums.length;
let prefix = 0;
for (let i = 0; i < nums.length; ++i) {
prefix = (prefix + nums[i]) % p;
let target = (prefix - rem + p) % p;
if (mods.has(target)) {
res = Math.min(res, i - mods.get(target));
}
mods.set(prefix, i);
}
return res < nums.length ? res : -1;
};