|
| 1 | +# [Problem 1590: Make Sum Divisible by P](https://leetcode.com/problems/make-sum-divisible-by-p/description/?envType=daily-question) |
| 2 | + |
| 3 | +## Initial thoughts (stream-of-consciousness) |
| 4 | +We need to remove the smallest contiguous subarray so that the remaining sum is divisible by p. Let total = sum(nums). If total % p == 0, return 0 immediately. Otherwise we need to remove a subarray whose sum % p equals total % p (call this need). So the problem becomes: find the shortest subarray with sum % p == need. Brute force O(n^2) checking all subarrays is too slow for n up to 1e5. |
| 5 | + |
| 6 | +Prefix sums modulo p can help: sum(i..j) % p = (pref[j] - pref[i-1]) % p. So for each prefix remainder pref_i, we want to find a previous prefix remainder pref_j such that (pref_i - pref_j) % p == need, equivalently pref_j == (pref_i - need) % p. We can keep a map from remainder -> latest index and update as we scan. Initialize map{0: -1} to represent empty prefix. Track minimal length. If minimal length equals the whole array, not allowed (must not remove whole array), return -1. |
| 7 | + |
| 8 | +## Refining the problem, round 2 thoughts |
| 9 | +- Edge cases: need == 0 (already divisible) -> return 0. If no valid subarray found -> return -1. |
| 10 | +- Must ensure we don't allow removing whole array: if best length == n -> return -1. |
| 11 | +- Implementation detail: use modulo arithmetic carefully: (pref - need) % p gives target remainder in Python (non-negative). |
| 12 | +- Complexity: single pass O(n) time, O(n) extra space for the map (at most n distinct remainders stored). Works for large nums since we only use remainders. |
| 13 | +- Alternative: sliding window doesn't apply directly because elements are arbitrary and we want modulo condition, not fixed target sum. |
| 14 | + |
| 15 | +## Attempted solution(s) |
| 16 | +```python |
| 17 | +class Solution: |
| 18 | + def minSubarray(self, nums: list[int], p: int) -> int: |
| 19 | + total = sum(nums) |
| 20 | + need = total % p |
| 21 | + if need == 0: |
| 22 | + return 0 |
| 23 | + |
| 24 | + pref = 0 |
| 25 | + best = len(nums) + 1 |
| 26 | + last_index = {0: -1} # remainder -> latest index |
| 27 | + |
| 28 | + for i, x in enumerate(nums): |
| 29 | + pref = (pref + x) % p |
| 30 | + # we need previous remainder such that (pref - prev) % p == need |
| 31 | + target = (pref - need) % p |
| 32 | + if target in last_index: |
| 33 | + length = i - last_index[target] |
| 34 | + if length < best: |
| 35 | + best = length |
| 36 | + # update with current prefix remainder (store latest index) |
| 37 | + last_index[pref] = i |
| 38 | + |
| 39 | + if best <= 0 or best > len(nums) - 1: |
| 40 | + # best == len(nums) means removing whole array (not allowed) |
| 41 | + return -1 |
| 42 | + return best |
| 43 | +``` |
| 44 | +- Notes about solution: |
| 45 | + - Approach: Use prefix-sum remainders and a hashmap from remainder to latest index to find shortest subarray whose sum % p == need in one pass. |
| 46 | + - Correctness: For each index i, if a previous index j has remainder equal to (pref_i - need) % p, then the subarray (j+1..i) has sum % p == need. We minimize i-j. |
| 47 | + - Time complexity: O(n) single pass. |
| 48 | + - Space complexity: O(n) for the hashmap (in worst case storing one entry per index). |
| 49 | + - Implementation details: Initialize map with 0 -> -1 to allow subarrays starting at index 0. Ensure we do not return length equal to n (can't remove entire array). |
0 commit comments