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;
};