|
| 1 | +# [Problem 3499: Maximize Active Section with Trade I](https://leetcode.com/problems/maximize-active-section-with-trade-i/description/?envType=daily-question) |
| 2 | + |
| 3 | +## Initial thoughts (stream-of-consciousness) |
| 4 | +We have a binary string s, and are allowed exactly one two-step "trade": (1) pick a contiguous block of '1's that is surrounded by '0's and turn it to '0's; (2) then pick a contiguous block of '0's that is surrounded by '1's (in the string after step 1) and turn it to '1's. We augment s with '1' at both ends conceptually, but those augmented ones don't count in the result. |
| 5 | + |
| 6 | +The naive thought: final ones count = original_ones - removed_ones_len + added_zero_len. But step (1) may merge adjacent zero blocks with the removed one block, so step (2) could flip a much larger zero-block (the merged one). In particular, if we remove a one-run whose left and right neighbors are zero-runs of lengths L and R, those will merge into a zero-run of length L + removed_len + R, and flipping that merged run adds L + removed_len + R back. The removed_len cancels: net gain = L + R. So one promising candidate for each removable one-run is total_ones + L + R. |
| 7 | + |
| 8 | +We can also consider flipping some other zero-run (not adjacent) after removing a one-run elsewhere; then net gain is total_ones - removed_len + some_zero_len. So for each removable one-run, consider both merged (L + R) and best non-adjacent zero-run (best_zero_len_not_adjacent - removed_len). We also must consider doing no trade. |
| 9 | + |
| 10 | +We need to enumerate runs and analyze efficiently. |
| 11 | + |
| 12 | +## Refining the problem, round 2 thoughts |
| 13 | +Plan: |
| 14 | +- Build t = '1' + s + '1' and compress into runs (char, length). |
| 15 | +- total_ones = s.count('1') (augmented ones should not be counted). |
| 16 | +- Identify zero-run entries (length, run_index) in the compressed t. |
| 17 | +- Identify removable one-runs in t: a run of '1' that has zeros both sides (i.e., neighbors are '0'). These are valid for step (1). |
| 18 | +- For each removable one-run at index i with length A and adjacent zero lengths L (left) and R (right): |
| 19 | + - merged candidate final = total_ones + L + R (removal merges L + A + R then flipping adds that; A cancels) |
| 20 | + - non-adjacent candidate final = total_ones - A + best_zero_len where best_zero_len is the largest zero-run length whose run index != i-1 and != i+1 (so it wasn't one of the two zeros merged). |
| 21 | +- Track maximum among all removable one-runs and also include baseline total_ones (no trade). |
| 22 | +- Implementation detail: to find best_zero_len_not_adjacent quickly, get the top-3 zero-runs (by length). For each i, pick the first top-K whose index is not i-1 or i+1. Top-3 suffices because at most two zero-runs are disallowed. |
| 23 | +- Complexity: building runs O(n), scanning zeros O(n), sorting zero-runs would be O(m log m) where m<=n, but we can extract top-3 in O(n) too. Overall O(n) time and O(n) memory. |
| 24 | + |
| 25 | +Edge cases: |
| 26 | +- No removable one-runs -> cannot trade -> answer = total_ones. |
| 27 | +- No zero-runs -> cannot flip any zero block -> no trade -> answer = total_ones. |
| 28 | +- Handle small n correctly. |
| 29 | + |
| 30 | +Now implement. |
| 31 | + |
| 32 | +## Attempted solution(s) |
| 33 | +```python |
| 34 | +class Solution: |
| 35 | + def maximizeActive(self, s: str) -> int: |
| 36 | + # Build augmented string t = '1' + s + '1' and compress into runs |
| 37 | + t = '1' + s + '1' |
| 38 | + runs = [] # list of (char, length) |
| 39 | + cur = t[0] |
| 40 | + cnt = 1 |
| 41 | + for ch in t[1:]: |
| 42 | + if ch == cur: |
| 43 | + cnt += 1 |
| 44 | + else: |
| 45 | + runs.append((cur, cnt)) |
| 46 | + cur = ch |
| 47 | + cnt = 1 |
| 48 | + runs.append((cur, cnt)) |
| 49 | + |
| 50 | + n = len(s) |
| 51 | + total_ones = s.count('1') |
| 52 | + |
| 53 | + # Collect zero runs (length, index in runs) |
| 54 | + zero_runs = [] |
| 55 | + for idx, (ch, ln) in enumerate(runs): |
| 56 | + if ch == '0': |
| 57 | + zero_runs.append((ln, idx)) |
| 58 | + |
| 59 | + # If there are no removable one-runs or no zero runs, answer is total_ones |
| 60 | + if not zero_runs: |
| 61 | + return total_ones |
| 62 | + |
| 63 | + # Get top-3 zero runs by length |
| 64 | + zero_runs_sorted = sorted(zero_runs, key=lambda x: x[0], reverse=True) |
| 65 | + topk = zero_runs_sorted[:3] # list of (len, idx) |
| 66 | + |
| 67 | + ans = total_ones |
| 68 | + |
| 69 | + # For each removable one-run (one-run having zeros both sides) |
| 70 | + m = len(runs) |
| 71 | + for i in range(m): |
| 72 | + ch, one_len = runs[i] |
| 73 | + if ch != '1': |
| 74 | + continue |
| 75 | + # must have neighbors and both zeros |
| 76 | + if i - 1 < 0 or i + 1 >= m: |
| 77 | + continue |
| 78 | + if runs[i - 1][0] != '0' or runs[i + 1][0] != '0': |
| 79 | + continue |
| 80 | + |
| 81 | + left_zero = runs[i - 1][1] |
| 82 | + right_zero = runs[i + 1][1] |
| 83 | + |
| 84 | + # Candidate 1: remove this one-run and then flip the merged zero-run (left + one + right) |
| 85 | + # net effect is total_ones + left_zero + right_zero |
| 86 | + ans = max(ans, total_ones + left_zero + right_zero) |
| 87 | + |
| 88 | + # Candidate 2: remove this one-run and flip the best zero-run that is NOT i-1 or i+1 |
| 89 | + banned = {i - 1, i + 1} |
| 90 | + chosen_zero_len = 0 |
| 91 | + for zl, zidx in topk: |
| 92 | + if zidx not in banned: |
| 93 | + chosen_zero_len = zl |
| 94 | + break |
| 95 | + if chosen_zero_len > 0: |
| 96 | + candidate = total_ones - one_len + chosen_zero_len |
| 97 | + ans = max(ans, candidate) |
| 98 | + |
| 99 | + return ans |
| 100 | + |
| 101 | +# For compatibility with LeetCode expected class/method naming: |
| 102 | +# LeetCode problem provides a function signature maximizeActive(self, s: str) -> int |
| 103 | +# If running as Solution().maximizeActive(s) it will work. |
| 104 | +``` |
| 105 | +- Notes: |
| 106 | + - We compress the augmented string t = '1' + s + '1', which naturally handles boundaries (augmented ones merge with edge ones if present). |
| 107 | + - total_ones is counted only from s (augmented ones are ignored in the final count). |
| 108 | + - For each removable one-run (a '1' run with zero neighbors), two main cases are considered: |
| 109 | + - Flipping the merged zero-block formed by removing that one-run → net increase = left_zero + right_zero (removed ones cancel). |
| 110 | + - Flipping some other zero-block not adjacent to the removed one-run → net increase = best_zero_len_not_adjacent - removed_one_len. |
| 111 | + - We take the maximum over all removable one-runs and compare with doing no trade. |
| 112 | + - Time complexity: O(n) to build runs and scan (sorting top zero runs is limited to extracting top-3; implemented as sort on zero runs which is O(m log m) worst-case but m ≤ n). Overall O(n log n) worst-case if sorting used; can be optimized to pure O(n) by scanning to get top-3 zero runs. |
| 113 | + - Space complexity: O(n) to store runs. |
0 commit comments