|
| 1 | +# [Problem 3578: Count Partitions With Max-Min Difference at Most K](https://leetcode.com/problems/count-partitions-with-max-min-difference-at-most-k/description/?envType=daily-question) |
| 2 | + |
| 3 | +## Initial thoughts (stream-of-consciousness) |
| 4 | +I need to count ways to cut the array into contiguous segments where each segment's max - min <= k. That suggests dynamic programming: dp[i] = number of ways to partition prefix of length i. For dp[i] we sum dp[j] for all j where segment j..i-1 is valid. Naively checking all j for each i is O(n^2) and could be too slow for n up to 5e4. |
| 5 | + |
| 6 | +Observation: for a fixed right end i-1, as j moves left the range max-min is nondecreasing (adding elements can't decrease max-min). So for each right i-1 there is a smallest j* such that any j >= j* yields a valid segment [j..i-1]. So dp[i] = sum_{j=j*..i-1} dp[j]. If we maintain prefix sums of dp, we can compute this sum in O(1) once j* is known. So the remaining challenge is to find j* quickly for each i. |
| 7 | + |
| 8 | +We can maintain a sliding window with two monotonic deques (one for max, one for min). Move the right pointer forward; while current window [L..i] violates (max-min > k) increment L and pop expired indices from deques. L will be nondecreasing across i, giving amortized O(n). So overall O(n) time. |
| 9 | + |
| 10 | +## Refining the problem, round 2 thoughts |
| 11 | +Edge cases: |
| 12 | +- dp indices and prefix sums: be careful with off-by-one. I'll use dp[0]=1 (empty prefix), dp[t] = number ways for first t elements (t from 1..n). When processing element at index i (0-based), we compute dp[i+1]. |
| 13 | +- When L==0 need to subtract zero from prefix sum. |
| 14 | +- Maintain deques storing indices; when moving L forward pop indices equal to L from both deques. |
| 15 | +- Use modulo 10**9+7 everywhere and ensure differences are normalized positive. |
| 16 | + |
| 17 | +Complexity: |
| 18 | +- Time: O(n) amortized since each index enters/pops deques at most once. |
| 19 | +- Space: O(n) for dp/prefix arrays and O(n) worst-case for deques but deques are at most n aggregated. |
| 20 | + |
| 21 | +## Attempted solution(s) |
| 22 | +```python |
| 23 | +from collections import deque |
| 24 | + |
| 25 | +class Solution: |
| 26 | + def countPartitions(self, nums: list[int], k: int) -> int: |
| 27 | + MOD = 10**9 + 7 |
| 28 | + n = len(nums) |
| 29 | + # dp[t] = number of ways to partition first t elements (t in [0..n]) |
| 30 | + dp = [0] * (n + 1) |
| 31 | + pref = [0] * (n + 1) # pref[t] = sum_{u=0..t} dp[u] |
| 32 | + dp[0] = 1 |
| 33 | + pref[0] = 1 |
| 34 | + |
| 35 | + L = 0 |
| 36 | + maxdq = deque() # indices with nums[...] in decreasing order |
| 37 | + mindq = deque() # indices with nums[...] in increasing order |
| 38 | + |
| 39 | + for i in range(n): |
| 40 | + x = nums[i] |
| 41 | + # maintain monotonic deques for max and min |
| 42 | + while maxdq and nums[maxdq[-1]] < x: |
| 43 | + maxdq.pop() |
| 44 | + maxdq.append(i) |
| 45 | + while mindq and nums[mindq[-1]] > x: |
| 46 | + mindq.pop() |
| 47 | + mindq.append(i) |
| 48 | + |
| 49 | + # shrink from left until window [L..i] satisfies max-min <= k |
| 50 | + while maxdq and mindq and nums[maxdq[0]] - nums[mindq[0]] > k: |
| 51 | + # if the leftmost index equals L, pop it from the corresponding deque |
| 52 | + if maxdq and maxdq[0] == L: |
| 53 | + maxdq.popleft() |
| 54 | + if mindq and mindq[0] == L: |
| 55 | + mindq.popleft() |
| 56 | + L += 1 |
| 57 | + |
| 58 | + # Now valid windows are those starting at any j in [L..i] |
| 59 | + left_pref = pref[L - 1] if L > 0 else 0 |
| 60 | + dp[i + 1] = (pref[i] - left_pref) % MOD |
| 61 | + pref[i + 1] = (pref[i] + dp[i + 1]) % MOD |
| 62 | + |
| 63 | + return dp[n] |
| 64 | +``` |
| 65 | +- Notes about the approach: |
| 66 | + - We use dp with prefix sums to get dp[i+1] = sum_{j=L..i} dp[j] = pref[i] - pref[L-1]. |
| 67 | + - Two monotonic deques maintain current window's maximum and minimum in O(1) time per update; incrementing L until the window is valid moves L monotonically, giving amortized O(n) total movement. |
| 68 | + - Time complexity: O(n). Space complexity: O(n). |
| 69 | + - All arithmetic is done modulo 10^9 + 7. |
0 commit comments