Before attempting this problem, you should be comfortable with:
Prefix Sums - Used to efficiently compute subarray sums in constant time
Modular Arithmetic - Understanding how remainders work and handling negative modulo results
Hash Maps - Used to store and look up prefix sum remainders for O(1) access
1. Brute Force
Intuition
We need to find the shortest subarray to remove so that the remaining elements sum to a multiple of p. The brute force approach tries every possible subarray length starting from 1. For each length, we slide a window across the array and check if removing that window leaves a sum divisible by p. We return the first (smallest) length l that works.
Algorithm
Compute the total sum of the array. If it is already divisible by p, return 0.
For each possible subarray length l from 1 to n - 1:
Use a sliding window to compute the sum of each subarray of length l.
For each window position, calculate the remaining sum (total minus window sum).
classSolution:defminSubarray(self, nums: List[int], p:int)->int:
n =len(nums)
totSum =sum(nums)if totSum % p ==0:return0for l inrange(1, n):
curSum =0for i inrange(n):
curSum += nums[i]if i >= l:
curSum -= nums[i - l]
remainSum = totSum - curSum
if remainSum % p ==0:return l
return-1
publicclassSolution{publicintminSubarray(int[] nums,int p){int n = nums.length;long totSum =0;for(int num : nums) totSum += num;if(totSum % p ==0)return0;for(int l =1; l < n; l++){long curSum =0;for(int i =0; i < n; i++){
curSum += nums[i];if(i >= l) curSum -= nums[i - l];long remainSum = totSum - curSum;if(remainSum % p ==0)return l;}}return-1;}}
classSolution{public:intminSubarray(vector<int>& nums,int p){int n = nums.size();longlong totSum =0;for(int num : nums) totSum += num;if(totSum % p ==0)return0;for(int l =1; l < n; l++){longlong curSum =0;for(int i =0; i < n; i++){
curSum += nums[i];if(i >= l) curSum -= nums[i - l];longlong remainSum = totSum - curSum;if(remainSum % p ==0)return l;}}return-1;}};
classSolution{/**
* @param {number[]} nums
* @param {number} p
* @return {number}
*/minSubarray(nums, p){const n = nums.length;let totSum = nums.reduce((a, b)=> a + b,0);if(totSum % p ===0)return0;for(let l =1; l < n; l++){let curSum =0;for(let i =0; i < n; i++){
curSum += nums[i];if(i >= l) curSum -= nums[i - l];const remainSum = totSum - curSum;if(remainSum % p ===0)return l;}}return-1;}}
publicclassSolution{publicintMinSubarray(int[] nums,int p){int n = nums.Length;long totSum =0;foreach(int num in nums) totSum += num;if(totSum % p ==0)return0;for(int l =1; l < n; l++){long curSum =0;for(int i =0; i < n; i++){
curSum += nums[i];if(i >= l) curSum -= nums[i - l];long remainSum = totSum - curSum;if(remainSum % p ==0)return l;}}return-1;}}
funcminSubarray(nums []int, p int)int{
n :=len(nums)
totSum :=0for_, num :=range nums {
totSum += num
}if totSum%p ==0{return0}for l :=1; l < n; l++{
curSum :=0for i :=0; i < n; i++{
curSum += nums[i]if i >= l {
curSum -= nums[i-l]}
remainSum := totSum - curSum
if remainSum%p ==0{return l
}}}return-1}
class Solution {funminSubarray(nums: IntArray, p: Int): Int {val n = nums.size
var totSum =0Lfor(num in nums) totSum += num
if(totSum % p ==0L)return0for(l in1 until n){var curSum =0Lfor(i in0 until n){
curSum += nums[i]if(i >= l) curSum -= nums[i - l]val remainSum = totSum - curSum
if(remainSum % p ==0L)return l
}}return-1}}
classSolution{funcminSubarray(_ nums:[Int],_ p:Int)->Int{let n = nums.count
var totSum =0for num in nums {
totSum += num
}if totSum % p ==0{return0}for l in1..<n {var curSum =0for i in0..<n {
curSum += nums[i]if i >= l {
curSum -= nums[i - l]}let remainSum = totSum - curSum
if remainSum % p ==0{return l
}}}return-1}}
Time & Space Complexity
Time complexity: O(n2)
Space complexity: O(1)
2. Prefix Sum
Intuition
If the total sum has remainder r when divided by p, we need to remove a subarray whose sum also has remainder r (mod p). This transforms the problem into finding the shortest subarray with a specific remainder. Using prefix sums, if the current prefix has remainder curSum, we look for an earlier prefix with remainder (curSum - r + p) % p. A hash map stores the most recent index for each remainder, allowing O(1) lookups.
Algorithm
Calculate the total sum and its remainder remain when divided by p. If remain is 0, return 0.
Initialize a hash map with {0: -1} to handle subarrays starting from index 0.
Iterate through the array, maintaining the running prefix sum modulo p.
For each position, compute the target prefix remainder: (curSum - remain + p) % p.
If this target exists in the map, update the minimum length.
Store the current prefix remainder and its index in the map.
Return the minimum length found, or -1 if no valid subarray exists (or if removing it would leave an empty array).
classSolution:defminSubarray(self, nums: List[int], p:int)->int:
total =sum(nums)
remain = total % p
if remain ==0:return0
res =len(nums)
cur_sum =0
remain_to_idx ={0:-1}for i, n inenumerate(nums):
cur_sum =(cur_sum + n)% p
prefix =(cur_sum - remain + p)% p
if prefix in remain_to_idx:
length = i - remain_to_idx[prefix]
res =min(res, length)
remain_to_idx[cur_sum]= i
return-1if res ==len(nums)else res
publicclassSolution{publicintminSubarray(int[] nums,int p){long total =0;for(int num : nums) total += num;int remain =(int)(total % p);if(remain ==0)return0;int res = nums.length;long curSum =0;Map<Integer,Integer> map =newHashMap<>();
map.put(0,-1);for(int i =0; i < nums.length; i++){
curSum =(curSum + nums[i])% p;int prefix =(int)((curSum - remain + p)% p);if(map.containsKey(prefix)){
res =Math.min(res, i - map.get(prefix));}
map.put((int)curSum, i);}return res == nums.length ?-1: res;}}
classSolution{public:intminSubarray(vector<int>& nums,int p){long total =0;for(int num : nums) total += num;int remain = total % p;if(remain ==0)return0;int res = nums.size();long curSum =0;
unordered_map<int,int> map;
map[0]=-1;for(int i =0; i < nums.size(); i++){
curSum =(curSum + nums[i])% p;int prefix =(curSum - remain + p)% p;if(map.count(prefix)){
res =min(res, i - map[prefix]);}
map[curSum]= i;}return res == nums.size()?-1: res;}};
classSolution{/**
* @param {number[]} nums
* @param {number} p
* @return {number}
*/minSubarray(nums, p){let total = nums.reduce((a, b)=> a + b,0);let remain = total % p;if(remain ===0)return0;let res = nums.length;let curSum =0;const map =newMap();
map.set(0,-1);for(let i =0; i < nums.length; i++){
curSum =(curSum + nums[i])% p;let prefix =(curSum - remain + p)% p;if(map.has(prefix)){
res = Math.min(res, i - map.get(prefix));}
map.set(curSum, i);}return res === nums.length ?-1: res;}}
publicclassSolution{publicintMinSubarray(int[] nums,int p){long total =0;foreach(int num in nums) total += num;int remain =(int)(total % p);if(remain ==0)return0;int res = nums.Length;long curSum =0;var map =newDictionary<int,int>();
map[0]=-1;for(int i =0; i < nums.Length; i++){
curSum =(curSum + nums[i])% p;int prefix =(int)((curSum - remain + p)% p);if(map.ContainsKey(prefix)){
res = Math.Min(res, i - map[prefix]);}
map[(int)curSum]= i;}return res == nums.Length ?-1: res;}}
funcminSubarray(nums []int, p int)int{
total :=0for_, num :=range nums {
total += num
}
remain := total % p
if remain ==0{return0}
res :=len(nums)
curSum :=0
m :=make(map[int]int)
m[0]=-1for i, num :=range nums {
curSum =(curSum + num)% p
prefix :=(curSum - remain + p)% p
if idx, ok := m[prefix]; ok {if i-idx < res {
res = i - idx
}}
m[curSum]= i
}if res ==len(nums){return-1}return res
}
class Solution {funminSubarray(nums: IntArray, p: Int): Int {var total =0Lfor(num in nums) total += num
val remain =(total % p).toInt()if(remain ==0)return0var res = nums.size
var curSum =0Lval map = HashMap<Int, Int>()
map[0]=-1for(i in nums.indices){
curSum =(curSum + nums[i])% p
val prefix =((curSum - remain + p)% p).toInt()if(map.containsKey(prefix)){
res =minOf(res, i - map[prefix]!!)}
map[curSum.toInt()]= i
}returnif(res == nums.size)-1else res
}}
classSolution{funcminSubarray(_ nums:[Int],_ p:Int)->Int{var total =0for num in nums {
total += num
}let remain = total % p
if remain ==0{return0}var res = nums.count
var curSum =0var map =[Int:Int]()
map[0]=-1for i in0..<nums.count {
curSum =(curSum + nums[i])% p
letprefix=(curSum - remain + p)% p
iflet idx = map[prefix]{
res =min(res, i - idx)}
map[curSum]= i
}return res == nums.count ?-1: res
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
Common Pitfalls
Forgetting to Handle Negative Modulo Results
When computing (curSum - remain) % p, the result can be negative in many programming languages. Always add p before taking modulo: (curSum - remain + p) % p. Failing to do this causes incorrect hash map lookups and wrong answers.
Not Initializing the Hash Map with {0: -1}
The hash map must start with {0: -1} to handle subarrays that start from index 0. Without this initialization, you cannot detect valid subarrays that begin at the first element of the array.
Returning the Wrong Value When No Valid Subarray Exists
If the minimum length found equals n (the entire array), you must return -1 because removing all elements leaves an empty array, which is invalid. A common mistake is forgetting this edge case and returning n instead.