🧠
Optimal Hash Map with Prefix Sum Remainders
💡 This approach uses a hash map to track prefix sum remainders modulo k, enabling O(n) time detection of qualifying subarrays by leveraging the mathematical property of remainders.
Intuition
If two prefix sums have the same remainder when divided by k, the subarray between them sums to a multiple of k.
Algorithm
- Initialize a hash map to store remainder to earliest index mapping, starting with remainder 0 at index -1.
- Iterate through nums, accumulating running sum.
- Calculate remainder of running sum modulo k (handle k=0 separately).
- If remainder seen before and subarray length >= 2, return true.
- Otherwise, store remainder with current index if not already stored.
- Return false if no qualifying subarray found.
💡 The hash map tracks where each remainder first appeared, so when it appears again, the subarray between those indices sums to a multiple of k.
def checkSubarraySum(nums, k):
remainder_map = {0: -1}
running_sum = 0
for i, num in enumerate(nums):
running_sum += num
if k != 0:
running_sum %= k
if running_sum in remainder_map:
if i - remainder_map[running_sum] > 1:
return True
else:
remainder_map[running_sum] = i
return False
# Example usage
if __name__ == '__main__':
print(checkSubarraySum([23, 2, 4, 6, 7], 6)) # True
Line Notes
remainder_map = {0: -1}Initialize map with remainder 0 at index -1 to handle subarrays from start
running_sum = 0Running sum accumulator
for i, num in enumerate(nums):Iterate over array with index
running_sum += numAdd current number to running sum
if k != 0:Avoid modulo by zero error
running_sum %= kKeep running sum modulo k
if running_sum in remainder_map:Check if remainder seen before
if i - remainder_map[running_sum] > 1:Ensure subarray length at least two
else: remainder_map[running_sum] = iStore first occurrence of this remainder
import java.util.*;
public class Solution {
public boolean checkSubarraySum(int[] nums, int k) {
Map<Integer, Integer> remainderMap = new HashMap<>();
remainderMap.put(0, -1);
int runningSum = 0;
for (int i = 0; i < nums.length; i++) {
runningSum += nums[i];
if (k != 0) {
runningSum %= k;
}
if (remainderMap.containsKey(runningSum)) {
if (i - remainderMap.get(runningSum) > 1) {
return true;
}
} else {
remainderMap.put(runningSum, i);
}
}
return false;
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.checkSubarraySum(new int[]{23, 2, 4, 6, 7}, 6)); // true
}
}
Line Notes
Map<Integer, Integer> remainderMap = new HashMap<>();Hash map to store remainder to index
remainderMap.put(0, -1);Initialize with remainder 0 at index -1
int runningSum = 0;Running sum accumulator
runningSum += nums[i];Add current element to running sum
if (k != 0) { runningSum %= k; }Modulo running sum by k if k not zero
if (remainderMap.containsKey(runningSum)) {Check if remainder seen before
if (i - remainderMap.get(runningSum) > 1) {Check subarray length at least two
else { remainderMap.put(runningSum, i); }Store first occurrence of remainder
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k) {
unordered_map<int, int> remainderMap;
remainderMap[0] = -1;
int runningSum = 0;
for (int i = 0; i < nums.size(); ++i) {
runningSum += nums[i];
if (k != 0) {
runningSum %= k;
}
if (remainderMap.find(runningSum) != remainderMap.end()) {
if (i - remainderMap[runningSum] > 1) {
return true;
}
} else {
remainderMap[runningSum] = i;
}
}
return false;
}
};
int main() {
Solution sol;
vector<int> nums = {23, 2, 4, 6, 7};
cout << boolalpha << sol.checkSubarraySum(nums, 6) << endl; // true
return 0;
}
Line Notes
unordered_map<int, int> remainderMap;Hash map for remainder to index
remainderMap[0] = -1;Initialize with remainder 0 at index -1
int runningSum = 0;Running sum accumulator
runningSum += nums[i];Add current element to running sum
if (k != 0) { runningSum %= k; }Modulo running sum by k if k not zero
if (remainderMap.find(runningSum) != remainderMap.end()) {Check if remainder seen before
if (i - remainderMap[runningSum] > 1) {Check subarray length at least two
else { remainderMap[runningSum] = i; }Store first occurrence of remainder
function checkSubarraySum(nums, k) {
const remainderMap = new Map();
remainderMap.set(0, -1);
let runningSum = 0;
for (let i = 0; i < nums.length; i++) {
runningSum += nums[i];
if (k !== 0) {
runningSum %= k;
}
if (remainderMap.has(runningSum)) {
if (i - remainderMap.get(runningSum) > 1) {
return true;
}
} else {
remainderMap.set(runningSum, i);
}
}
return false;
}
// Example usage
console.log(checkSubarraySum([23, 2, 4, 6, 7], 6)); // true
Line Notes
const remainderMap = new Map();Hash map to store remainder to index
remainderMap.set(0, -1);Initialize with remainder 0 at index -1
let runningSum = 0;Running sum accumulator
runningSum += nums[i];Add current element to running sum
if (k !== 0) { runningSum %= k; }Modulo running sum by k if k not zero
if (remainderMap.has(runningSum)) {Check if remainder seen before
if (i - remainderMap.get(runningSum) > 1) {Check subarray length at least two
else { remainderMap.set(runningSum, i); }Store first occurrence of remainder
TimeO(n)
SpaceO(min(n, k))
Single pass through array with constant time hash map operations; space depends on distinct remainders stored.
💡 For n=100000, this means 100000 operations, which is efficient and scalable.
Interview Verdict: Accepted / Optimal solution
This is the best approach to implement in interviews due to its efficiency and elegance.