diff --git a/solution/3500-3599/3501.Maximize Active Section with Trade II/README.md b/solution/3500-3599/3501.Maximize Active Section with Trade II/README.md index cbaba27c1259a..eb8f7a766189c 100644 --- a/solution/3500-3599/3501.Maximize Active Section with Trade II/README.md +++ b/solution/3500-3599/3501.Maximize Active Section with Trade II/README.md @@ -173,25 +173,556 @@ tags: #### Python3 ```python - +import bisect +from typing import List + +class Solution: + def maxActiveSectionsAfterTrade(self, s: str, queries: List[List[int]]) -> List[int]: + n = len(s) + total_ones = 0 + + zero_blocks = [] + start = -1 + + # Find all contiguous blocks of '0's and count total '1's + for i, char in enumerate(s): + if char == '0': + if start == -1: + start = i + else: + total_ones += 1 + if start != -1: + zero_blocks.append((start, i - 1)) + start = -1 + + if start != -1: + zero_blocks.append((start, n - 1)) + + m = len(zero_blocks) + starts = [block[0] for block in zero_blocks] + ends = [block[1] for block in zero_blocks] + + # Precompute pair sums and build a Sparse Table for O(1) Range Maximum Queries + st = [] + log2_arr = [] + + if m > 1: + pair_sums = [(ends[i] - starts[i] + 1) + (ends[i+1] - starts[i+1] + 1) for i in range(m - 1)] + + # Equivalent to finding log2(m - 1) + max_log = (m - 1).bit_length() + st = [[0] * max_log for _ in range(m - 1)] + log2_arr = [0] * (m + 1) + + for i in range(2, m + 1): + log2_arr[i] = log2_arr[i // 2] + 1 + + for i in range(m - 1): + st[i][0] = pair_sums[i] + + for j in range(1, max_log): + for i in range(m - 1): + if i + (1 << j) <= m - 1: + st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]) + + answer = [] + + for l, r in queries: + # Binary search to find the first block ending >= l + first_idx = bisect.bisect_left(ends, l) + first_block = first_idx if first_idx < m else -1 + + # Binary search to find the last block starting <= r + # bisect_right returns insertion point, so we subtract 1 for the element <= r + last_idx = bisect.bisect_right(starts, r) + last_block = last_idx - 1 if last_idx > 0 else -1 + + max_gain = 0 + + if first_block != -1 and last_block != -1 and first_block < last_block: + if first_block + 1 == last_block: + # Exactly two blocks intersect the query + len1 = min(r, ends[first_block]) - max(l, starts[first_block]) + 1 + len2 = min(r, ends[last_block]) - max(l, starts[last_block]) + 1 + max_gain = len1 + len2 + else: + # More than two blocks intersect the query + + # Left boundary pair (first intersecting block + the one immediately after) + len_first = min(r, ends[first_block]) - max(l, starts[first_block]) + 1 + len_second = ends[first_block + 1] - starts[first_block + 1] + 1 + max_gain = max(max_gain, len_first + len_second) + + # Right boundary pair (last intersecting block + the one immediately before) + len_second_last = ends[last_block - 1] - starts[last_block - 1] + 1 + len_last = min(r, ends[last_block]) - max(l, starts[last_block]) + 1 + max_gain = max(max_gain, len_second_last + len_last) + + # Intermediate pairs fully inside the interval + L = first_block + 1 + R = last_block - 2 + if L <= R: + j = log2_arr[R - L + 1] + max_internal = max(st[L][j], st[R - (1 << j) + 1][j]) + max_gain = max(max_gain, max_internal) + + answer.append(total_ones + max_gain) + + return answer ``` #### Java ```java - +import java.util.ArrayList; +import java.util.List; + +class Solution { + public List maxActiveSectionsAfterTrade(String s, int[][] queries) { + int n = s.length(); + int totalOnes = 0; + + List zeroBlocksList = new ArrayList<>(); + int start = -1; + + // Find all contiguous blocks of '0's and count total '1's + for (int i = 0; i < n; i++) { + if (s.charAt(i) == '0') { + if (start == -1) start = i; + } else { + totalOnes++; + if (start != -1) { + zeroBlocksList.add(new int[]{start, i - 1}); + start = -1; + } + } + } + if (start != -1) { + zeroBlocksList.add(new int[]{start, n - 1}); + } + + int m = zeroBlocksList.size(); + int[] starts = new int[m]; + int[] ends = new int[m]; + for (int i = 0; i < m; i++) { + starts[i] = zeroBlocksList.get(i)[0]; + ends[i] = zeroBlocksList.get(i)[1]; + } + + // Precompute pair sums and build a Sparse Table for O(1) Range Maximum Queries + int[][] st = null; + int[] log2 = null; + if (m > 1) { + int[] pairSums = new int[m - 1]; + for (int i = 0; i < m - 1; i++) { + pairSums[i] = (ends[i] - starts[i] + 1) + (ends[i + 1] - starts[i + 1] + 1); + } + + int maxLog = (int) (Math.log(m - 1) / Math.log(2)) + 1; + st = new int[m - 1][maxLog]; + log2 = new int[m + 1]; + + for (int i = 2; i <= m; i++) { + log2[i] = log2[i / 2] + 1; + } + + for (int i = 0; i < m - 1; i++) { + st[i][0] = pairSums[i]; + } + + for (int j = 1; j < maxLog; j++) { + for (int i = 0; i + (1 << j) <= m - 1; i++) { + st[i][j] = Math.max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); + } + } + } + + List answer = new ArrayList<>(queries.length); + + for (int i = 0; i < queries.length; i++) { + int l = queries[i][0]; + int r = queries[i][1]; + + // Binary search to find the first block ending >= l + int firstBlock = lowerBoundEnds(ends, l); + // Binary search to find the last block starting <= r + int lastBlock = upperBoundStarts(starts, r); + + int maxGain = 0; + + if (firstBlock != -1 && lastBlock != -1 && firstBlock < lastBlock) { + if (firstBlock + 1 == lastBlock) { + // Exactly two blocks intersect the query + int len1 = Math.min(r, ends[firstBlock]) - Math.max(l, starts[firstBlock]) + 1; + int len2 = Math.min(r, ends[lastBlock]) - Math.max(l, starts[lastBlock]) + 1; + maxGain = len1 + len2; + } else { + // More than two blocks intersect the query + + // Left boundary pair (first intersecting block + the one immediately after) + int lenFirst = Math.min(r, ends[firstBlock]) - Math.max(l, starts[firstBlock]) + 1; + int lenSecond = ends[firstBlock + 1] - starts[firstBlock + 1] + 1; + maxGain = Math.max(maxGain, lenFirst + lenSecond); + + // Right boundary pair (last intersecting block + the one immediately before) + int lenSecondLast = ends[lastBlock - 1] - starts[lastBlock - 1] + 1; + int lenLast = Math.min(r, ends[lastBlock]) - Math.max(l, starts[lastBlock]) + 1; + maxGain = Math.max(maxGain, lenSecondLast + lenLast); + + // Intermediate pairs fully inside the interval + int L = firstBlock + 1; + int R = lastBlock - 2; + if (L <= R) { + int j = log2[R - L + 1]; + int maxInternal = Math.max(st[L][j], st[R - (1 << j) + 1][j]); + maxGain = Math.max(maxGain, maxInternal); + } + } + } + + answer.add(totalOnes + maxGain); + } + + return answer; + } + + // Finds the first index where ends[i] >= target + private int lowerBoundEnds(int[] ends, int target) { + int low = 0, high = ends.length - 1; + int ans = -1; + while (low <= high) { + int mid = low + (high - low) / 2; + if (ends[mid] >= target) { + ans = mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + return ans; + } + + // Finds the last index where starts[i] <= target + private int upperBoundStarts(int[] starts, int target) { + int low = 0, high = starts.length - 1; + int ans = -1; + while (low <= high) { + int mid = low + (high - low) / 2; + if (starts[mid] <= target) { + ans = mid; + low = mid + 1; + } else { + high = mid - 1; + } + } + return ans; + } +} ``` #### C++ ```cpp - +#include +#include +#include +#include + +using namespace std; + +class Solution { +private: + // Finds the first index where ends[i] >= target + int lowerBoundEnds(const vector& ends, int target) { + int low = 0, high = (int)ends.size() - 1; + int ans = -1; + while (low <= high) { + int mid = low + (high - low) / 2; + if (ends[mid] >= target) { + ans = mid; + high = mid - 1; + } else { + low = mid + 1; + } + } + return ans; + } + + // Finds the last index where starts[i] <= target + int upperBoundStarts(const vector& starts, int target) { + int low = 0, high = (int)starts.size() - 1; + int ans = -1; + while (low <= high) { + int mid = low + (high - low) / 2; + if (starts[mid] <= target) { + ans = mid; + low = mid + 1; + } else { + high = mid - 1; + } + } + return ans; + } + +public: + vector maxActiveSectionsAfterTrade(string s, vector>& queries) { + int n = s.length(); + int totalOnes = 0; + + vector> zeroBlocksList; + int start = -1; + + // Find all contiguous blocks of '0's and count total '1's + for (int i = 0; i < n; i++) { + if (s[i] == '0') { + if (start == -1) start = i; + } else { + totalOnes++; + if (start != -1) { + zeroBlocksList.push_back({start, i - 1}); + start = -1; + } + } + } + if (start != -1) { + zeroBlocksList.push_back({start, n - 1}); + } + + int m = zeroBlocksList.size(); + vector starts(m); + vector ends(m); + for (int i = 0; i < m; i++) { + starts[i] = zeroBlocksList[i].first; + ends[i] = zeroBlocksList[i].second; + } + + // Precompute pair sums and build a Sparse Table for O(1) Range Maximum Queries + vector> st; + vector log2; + + if (m > 1) { + vector pairSums(m - 1); + for (int i = 0; i < m - 1; i++) { + pairSums[i] = (ends[i] - starts[i] + 1) + (ends[i + 1] - starts[i + 1] + 1); + } + + int maxLog = 0; + while ((1 << maxLog) <= (m - 1)) { + maxLog++; + } + + st.assign(m - 1, vector(maxLog, 0)); + log2.assign(m + 1, 0); + + for (int i = 2; i <= m; i++) { + log2[i] = log2[i / 2] + 1; + } + + for (int i = 0; i < m - 1; i++) { + st[i][0] = pairSums[i]; + } + + for (int j = 1; j < maxLog; j++) { + for (int i = 0; i + (1 << j) <= m - 1; i++) { + st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); + } + } + } + + vector answer; + answer.reserve(queries.size()); + + for (const auto& query : queries) { + int l = query[0]; + int r = query[1]; + + // Binary search to find the first block ending >= l + int firstBlock = lowerBoundEnds(ends, l); + // Binary search to find the last block starting <= r + int lastBlock = upperBoundStarts(starts, r); + + int maxGain = 0; + + if (firstBlock != -1 && lastBlock != -1 && firstBlock < lastBlock) { + if (firstBlock + 1 == lastBlock) { + // Exactly two blocks intersect the query + int len1 = min(r, ends[firstBlock]) - max(l, starts[firstBlock]) + 1; + int len2 = min(r, ends[lastBlock]) - max(l, starts[lastBlock]) + 1; + maxGain = len1 + len2; + } else { + // More than two blocks intersect the query + + // Left boundary pair (first intersecting block + the one immediately after) + int lenFirst = min(r, ends[firstBlock]) - max(l, starts[firstBlock]) + 1; + int lenSecond = ends[firstBlock + 1] - starts[firstBlock + 1] + 1; + maxGain = max(maxGain, lenFirst + lenSecond); + + // Right boundary pair (last intersecting block + the one immediately before) + int lenSecondLast = ends[lastBlock - 1] - starts[lastBlock - 1] + 1; + int lenLast = min(r, ends[lastBlock]) - max(l, starts[lastBlock]) + 1; + maxGain = max(maxGain, lenSecondLast + lenLast); + + // Intermediate pairs fully inside the interval + int L = firstBlock + 1; + int R = lastBlock - 2; + if (L <= R) { + int j = log2[R - L + 1]; + int maxInternal = max(st[L][j], st[R - (1 << j) + 1][j]); + maxGain = max(maxGain, maxInternal); + } + } + } + + answer.push_back(totalOnes + maxGain); + } + + return answer; + } +}; ``` #### Go ```go - +import "sort" + +func minInt(a, b int) int { + if a < b { + return a + } + return b +} + +func maxInt(a, b int) int { + if a > b { + return a + } + return b +} + +func maxActiveSectionsAfterTrade(s string, queries [][]int) []int { + n := len(s) + totalOnes := 0 + + var starts []int + var ends []int + start := -1 + + // Find all contiguous blocks of '0's and count total '1's + for i := 0; i < n; i++ { + if s[i] == '0' { + if start == -1 { + start = i + } + } else { + totalOnes++ + if start != -1 { + starts = append(starts, start) + ends = append(ends, i-1) + start = -1 + } + } + } + if start != -1 { + starts = append(starts, start) + ends = append(ends, n-1) + } + + m := len(starts) + var st [][]int + var log2 []int + + // Precompute pair sums and build a Sparse Table for O(1) Range Maximum Queries + if m > 1 { + pairSums := make([]int, m-1) + for i := 0; i < m-1; i++ { + pairSums[i] = (ends[i] - starts[i] + 1) + (ends[i+1] - starts[i+1] + 1) + } + + maxLog := 0 + for (1 << maxLog) <= (m - 1) { + maxLog++ + } + + st = make([][]int, m-1) + for i := range st { + st[i] = make([]int, maxLog) + } + + log2 = make([]int, m+1) + for i := 2; i <= m; i++ { + log2[i] = log2[i/2] + 1 + } + + for i := 0; i < m-1; i++ { + st[i][0] = pairSums[i] + } + + for j := 1; j < maxLog; j++ { + for i := 0; i+(1<= l + firstIdx := sort.Search(m, func(j int) bool { + return ends[j] >= l + }) + firstBlock := firstIdx + if firstIdx == m { + firstBlock = -1 + } + + // Binary search to find the last block starting <= r + // sort.Search finds the first block > r, so we subtract 1 + lastIdx := sort.Search(m, func(j int) bool { + return starts[j] > r + }) + lastBlock := lastIdx - 1 + + maxGain := 0 + + if firstBlock != -1 && lastBlock != -1 && firstBlock < lastBlock { + if firstBlock+1 == lastBlock { + // Exactly two blocks intersect the query + len1 := minInt(r, ends[firstBlock]) - maxInt(l, starts[firstBlock]) + 1 + len2 := minInt(r, ends[lastBlock]) - maxInt(l, starts[lastBlock]) + 1 + maxGain = len1 + len2 + } else { + // More than two blocks intersect the query + + // Left boundary pair (first intersecting block + the one immediately after) + lenFirst := minInt(r, ends[firstBlock]) - maxInt(l, starts[firstBlock]) + 1 + lenSecond := ends[firstBlock+1] - starts[firstBlock+1] + 1 + maxGain = maxInt(maxGain, lenFirst+lenSecond) + + // Right boundary pair (last intersecting block + the one immediately before) + lenSecondLast := ends[lastBlock-1] - starts[lastBlock-1] + 1 + lenLast := minInt(r, ends[lastBlock]) - maxInt(l, starts[lastBlock]) + 1 + maxGain = maxInt(maxGain, lenSecondLast+lenLast) + + // Intermediate pairs fully inside the interval + L := firstBlock + 1 + R := lastBlock - 2 + if L <= R { + j := log2[R-L+1] + maxInternal := maxInt(st[L][j], st[R-(1<