diff --git a/problems/1658-minimum-operations-to-reduce-x-to-zero/analysis.md b/problems/1658-minimum-operations-to-reduce-x-to-zero/analysis.md new file mode 100644 index 0000000..b367d79 --- /dev/null +++ b/problems/1658-minimum-operations-to-reduce-x-to-zero/analysis.md @@ -0,0 +1,82 @@ +# 1658. Minimum Operations to Reduce X to Zero + +[LeetCode Link](https://leetcode.com/problems/minimum-operations-to-reduce-x-to-zero/) + +Difficulty: Medium +Topics: Array, Hash Table, Binary Search, Sliding Window, Prefix Sum +Acceptance Rate: 41.7% + +## Hints + +### Hint 1 + +The operations only ever peel elements off the two ends, so whatever you remove is always a **prefix plus a suffix** of the original array — never anything from the middle. That means the order of your removals doesn't matter, only *how many* you take from the left and *how many* from the right. Try to restate the problem in terms of those two counts instead of a sequence of moves. + +Also notice what's missing from the problem: nothing asks you to actually mutate the array. Simulating removals (or exploring both choices recursively) is the trap that makes this look harder than it is. + +### Hint 2 + +Searching directly over "prefix of length `i` + suffix of length `j` with sum exactly `x`" is doable with prefix sums and a two-pointer scan, but there's a slicker reformulation. The elements you **keep** are exactly the ones you didn't remove — and since the removals are a prefix and a suffix, the kept elements form a **contiguous subarray** in the middle. + +So: if `total` is the sum of the whole array, and the removed part sums to `x`, what must the middle subarray sum to? And if you want to *minimize* the number removed, what do you want to do to the middle? + +### Hint 3 + +Minimize removals ⟺ **maximize the length of a contiguous subarray whose sum equals `total - x`**. Call that target `target = total - x`. If you find the longest such subarray with length `L`, the answer is `n - L`. + +The last piece is why a sliding window is legal here: the constraint `1 <= nums[i]` means every element is strictly positive, so the running window sum is **monotonically increasing** as you extend the right end and **decreasing** as you advance the left end. That monotonicity is exactly what lets you move `left` forward without ever needing to back up — giving an O(n) scan instead of a hash-map-of-prefix-sums lookup. (The hash map version works too and is the approach you'd need if negatives were allowed.) + +Watch the two degenerate targets: `target < 0` and `target == 0`. + +## Approach + +**Step 1 — Reframe.** Removing a prefix summing to `p` and a suffix summing to `s` with `p + s = x` leaves behind a contiguous middle subarray summing to `total - x`. Minimizing `(prefix length + suffix length)` is the same as maximizing the middle subarray's length. So the whole problem collapses to: + +> Find the **longest** contiguous subarray of `nums` with sum exactly `target = total - x`. + +If such a subarray of length `L` exists, the answer is `n - L`. If none exists, the answer is `-1`. + +**Step 2 — Handle the degenerate targets.** + +- `target < 0`: the entire array sums to less than `x`, so even removing everything can't reach `x`. Return `-1` immediately. (This also guards the sliding window, which assumes a non-negative target.) +- `target == 0`: you must remove the entire array. The "longest subarray summing to 0" is the empty one, length `0`, so the answer is `n`. Returning `n` up front keeps the main loop's `sum == target` check dealing only with non-empty windows. + +**Step 3 — Slide the window.** Maintain `left`, `right`, and the running `sum` of `nums[left..right]`. + +- Extend: add `nums[right]` to `sum`. +- Shrink: while `sum > target`, subtract `nums[left]` and advance `left`. Since every element is `>= 1`, each subtraction strictly decreases `sum`, so this terminates, and `sum` can never skip past `target` from below — we check it right after shrinking. +- Record: if `sum == target`, update the best length with `right - left + 1`. + +Each index enters and leaves the window at most once, so the pointers advance a total of at most `2n` times. + +**Worked example** — `nums = [3,2,20,1,1,3]`, `x = 10`: + +`total = 30`, so `target = 30 - 10 = 20`. + +| right | value | sum before shrink | shrink action | window | sum | best | +|---|---|---|---|---|---|---| +| 0 | 3 | 3 | — | `[3]` | 3 | — | +| 1 | 2 | 5 | — | `[3,2]` | 5 | — | +| 2 | 20 | 25 | drop 3, drop 2 | `[20]` | 20 | **1** | +| 3 | 1 | 21 | drop 20 | `[1]` | 1 | 1 | +| 4 | 1 | 2 | — | `[1,1]` | 2 | 1 | +| 5 | 3 | 5 | — | `[1,1,3]` | 5 | 1 | + +Longest window is `[20]` with `L = 1`, so the answer is `6 - 1 = 5` — matching the expected output (remove `3,2` from the left and `1,1,3` from the right). + +**Why not simulate?** Greedily taking the smaller end fails (Example 3 forces you to remove a `3` early even though `1`s sit on the right), and branching on both ends is `O(2^n)`. Memoizing on `(left, right)` is `O(n^2)` states — too slow for `n = 10^5`. The reframing is what buys the linear scan. + +## Complexity Analysis + +Time Complexity: O(n) — one pass to compute `total`, then one pass where `right` advances `n` times and `left` advances at most `n` times total. +Space Complexity: O(1) — only a handful of integer accumulators; the input is never copied or mutated. + +## Edge Cases + +- **`x > total` (`target < 0`)**: impossible — removing every element still leaves `x` positive. Must return `-1` *before* entering the window loop, otherwise `sum > target` would be true even for an empty window and `left` would run past `right` forever chasing an unreachable target. +- **`x == total` (`target == 0`)**: the answer is `n`, not `-1`. Easy to lose if you only look for non-empty windows, since the matching "middle" subarray here is the empty one. +- **No subarray sums to `target` even though `target > 0`**: e.g. `nums = [2,2,2], x = 3` → `target = 3`, but every subarray sum is even. The window never hits equality, so `best` stays unset and the answer is `-1`. Don't confuse "no window found" with "window of length 0". +- **Single-element array**: `[5], x = 5` → `1`; `[5], x = 3` → `-1`. Confirms the loop handles `n = 1` without special casing. +- **Answer uses only one end**: `nums = [1,1,4,2,3], x = 5` takes purely a suffix. The reformulation covers this automatically because the middle subarray is simply flush against one edge — no separate "prefix-only / suffix-only" branch needed. +- **Integer sizes**: `n <= 10^5` and `nums[i] <= 10^4` put `total` at up to `10^9`, and `x <= 10^9`, so `total - x` fits comfortably in Go's `int` (64-bit on all mainstream platforms). No overflow concern here, but it's worth a moment's thought since the sums are near the 32-bit boundary. +- **All elements positive is load-bearing**: the sliding window is only correct because of it. If negatives were allowed you'd need the prefix-sum + hash-map variant (store the *earliest* index for each prefix sum, then maximize `i - firstIndex[prefix-target]`), which is the Hash Table topic tag hinting at the general version. diff --git a/problems/1658-minimum-operations-to-reduce-x-to-zero/problem.md b/problems/1658-minimum-operations-to-reduce-x-to-zero/problem.md new file mode 100644 index 0000000..b0570b7 --- /dev/null +++ b/problems/1658-minimum-operations-to-reduce-x-to-zero/problem.md @@ -0,0 +1,58 @@ +--- +number: "1658" +frontend_id: "1658" +title: "Minimum Operations to Reduce X to Zero" +slug: "minimum-operations-to-reduce-x-to-zero" +difficulty: "Medium" +topics: + - "Array" + - "Hash Table" + - "Binary Search" + - "Sliding Window" + - "Prefix Sum" +acceptance_rate: 4170.9 +is_premium: false +created_at: "2026-09-23T04:58:04.824531+00:00" +fetched_at: "2026-09-23T04:58:04.824531+00:00" +link: "https://leetcode.com/problems/minimum-operations-to-reduce-x-to-zero/" +date: "2026-09-23" +--- + +# 1658. Minimum Operations to Reduce X to Zero + +You are given an integer array `nums` and an integer `x`. In one operation, you can either remove the leftmost or the rightmost element from the array `nums` and subtract its value from `x`. Note that this **modifies** the array for future operations. + +Return _the**minimum number** of operations to reduce _`x` _to**exactly**_ `0` _if it is possible_ _, otherwise, return_`-1`. + + + +**Example 1:** + + + **Input:** nums = [1,1,4,2,3], x = 5 + **Output:** 2 + **Explanation:** The optimal solution is to remove the last two elements to reduce x to zero. + + +**Example 2:** + + + **Input:** nums = [5,6,7,8,9], x = 4 + **Output:** -1 + + +**Example 3:** + + + **Input:** nums = [3,2,20,1,1,3], x = 10 + **Output:** 5 + **Explanation:** The optimal solution is to remove the last three elements and the first two elements (5 operations in total) to reduce x to zero. + + + + +**Constraints:** + + * `1 <= nums.length <= 105` + * `1 <= nums[i] <= 104` + * `1 <= x <= 109` diff --git a/problems/1658-minimum-operations-to-reduce-x-to-zero/solution_daily_20260923.go b/problems/1658-minimum-operations-to-reduce-x-to-zero/solution_daily_20260923.go new file mode 100644 index 0000000..11379b4 --- /dev/null +++ b/problems/1658-minimum-operations-to-reduce-x-to-zero/solution_daily_20260923.go @@ -0,0 +1,49 @@ +package main + +// 1658. Minimum Operations to Reduce X to Zero +// +// Removals only ever come off the two ends, so the removed elements form a +// prefix plus a suffix and the kept elements form a contiguous middle +// subarray. Removing a total of x therefore means the middle sums to +// target = total - x, and minimizing the number of removals is the same as +// maximizing that middle subarray's length: the answer is n - longestLen. +// +// Every nums[i] is >= 1, so a window's sum grows when the right end extends +// and shrinks when the left end advances. That monotonicity lets a single +// sliding window find the longest subarray summing to target in O(n) time +// and O(1) space. +func minOperations(nums []int, x int) int { + total := 0 + for _, v := range nums { + total += v + } + + target := total - x + if target < 0 { + // Even removing every element leaves x positive. + return -1 + } + if target == 0 { + // The whole array must go; the matching middle subarray is empty. + return len(nums) + } + + longest := -1 + sum, left := 0, 0 + for right, v := range nums { + sum += v + // target > 0 here, so this never shrinks past an empty window. + for sum > target { + sum -= nums[left] + left++ + } + if sum == target && right-left+1 > longest { + longest = right - left + 1 + } + } + + if longest < 0 { + return -1 + } + return len(nums) - longest +} diff --git a/problems/1658-minimum-operations-to-reduce-x-to-zero/solution_daily_20260923_test.go b/problems/1658-minimum-operations-to-reduce-x-to-zero/solution_daily_20260923_test.go new file mode 100644 index 0000000..b57a917 --- /dev/null +++ b/problems/1658-minimum-operations-to-reduce-x-to-zero/solution_daily_20260923_test.go @@ -0,0 +1,40 @@ +package main + +import "testing" + +func TestSolution(t *testing.T) { + tests := []struct { + name string + nums []int + x int + expected int + }{ + {"example 1: suffix only, remove last two", []int{1, 1, 4, 2, 3}, 5, 2}, + {"example 2: x exceeds total sum", []int{5, 6, 7, 8, 9}, 4, -1}, + {"example 3: two from the left and three from the right", []int{3, 2, 20, 1, 1, 3}, 10, 5}, + {"edge case: x equals total sum, remove everything", []int{1, 1, 4, 2, 3}, 11, 5}, + {"edge case: target reachable in value but not as a subarray sum", []int{2, 2, 2}, 3, -1}, + {"edge case: single element matches x exactly", []int{5}, 5, 1}, + {"edge case: single element smaller than x", []int{5}, 3, -1}, + {"edge case: one operation off the left end", []int{5, 2, 3, 1, 1}, 5, 1}, + {"edge case: prefer the longer of two matching windows", []int{1, 1, 1, 1, 1}, 2, 2}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + nums := make([]int, len(tt.nums)) + copy(nums, tt.nums) + + result := minOperations(nums, tt.x) + if result != tt.expected { + t.Errorf("minOperations(%v, %d) = %v, want %v", tt.nums, tt.x, result, tt.expected) + } + for i := range nums { + if nums[i] != tt.nums[i] { + t.Errorf("minOperations mutated input: got %v, want %v", nums, tt.nums) + break + } + } + }) + } +}