3721. Longest Balanced Subarray II
- Array
- Hash Table
- Segment Tree
TIME
O(n log n)
SPACE
O(n)
Intuition
For each left boundary l, we need to find the rightmost r where distinctOdd[l..r] == distinctEven[l..r]. Define diff[r] = distinctOdd - distinctEven for subarray [l..r]. We need to find rightmost r where diff[r] == 0.
Solution
Transform to balance array: For each position, track balance = (distinct odds) - (distinct evens). A subarray is balanced when balance = 0.
Segment tree with lazy propagation:
- Track min and max in ranges to quickly find if 0 exists
- Use lazy propagation for efficient range updates
Sliding window on left boundary:
- For each
l, find rightmostrwhere balance = 0 using segment tree - When moving
ltol+1, update the contribution ofnums[l]
- For each
First occurrence tracking:
- For each value, track positions in a deque
- When a value's first occurrence moves, update the segment tree range
Key Insight
When left boundary moves from l to l+1:
- If
nums[l]was the first occurrence of that value in[l, n-1] - Its contribution (+1 for odd, -1 for even) needs to be removed from range
[l, next_occurrence-1]
Complexity
- Time: O(n log n), each position processed once, segment tree operations are O(log n)
- Space: O(n), segment tree and position tracking
Edge Cases
- All even or all odd → return 0
- Single element → return 0
- All same value → return 0
Code
from typing import List
from collections import defaultdict, deque
class SegmentTree:
def __init__(self, n):
self.n = n
self.minv = [0] * (4 * n)
self.maxv = [0] * (4 * n)
self.lazy = [0] * (4 * n)
def _push(self, idx):
if self.lazy[idx] != 0:
for child in (idx * 2, idx * 2 + 1):
self.minv[child] += self.lazy[idx]
self.maxv[child] += self.lazy[idx]
self.lazy[child] += self.lazy[idx]
self.lazy[idx] = 0
def _pull(self, idx):
self.minv[idx] = min(self.minv[idx * 2], self.minv[idx * 2 + 1])
self.maxv[idx] = max(self.maxv[idx * 2], self.maxv[idx * 2 + 1])
def range_add(self, l, r, val, idx=1, nl=0, nr=None):
if nr is None:
nr = self.n - 1
if r < nl or nr < l:
return
if l <= nl and nr <= r:
self.minv[idx] += val
self.maxv[idx] += val
self.lazy[idx] += val
return
self._push(idx)
mid = (nl + nr) // 2
self.range_add(l, r, val, idx * 2, nl, mid)
self.range_add(l, r, val, idx * 2 + 1, mid + 1, nr)
self._pull(idx)
def find_zero(self, ql, idx=1, nl=0, nr=None):
if nr is None:
nr = self.n - 1
if nr < ql or self.minv[idx] > 0 or self.maxv[idx] < 0:
return -1
if nl == nr:
return nl
self._push(idx)
mid = (nl + nr) // 2
res = self.find_zero(ql, idx * 2 + 1, mid + 1, nr)
if res != -1:
return res
return self.find_zero(ql, idx * 2, nl, mid)
class Solution:
def longestBalanced(self, nums: List[int]) -> int:
"""
3721. Longest Balanced Subarray II
Time: O(n log n)
Space: O(n)
"""
n = len(nums)
pos = defaultdict(deque)
for i, v in enumerate(nums):
pos[v].append(i)
st = SegmentTree(n)
for v, dq in pos.items():
sign = 1 if v % 2 else -1
st.range_add(dq[0], n - 1, sign)
ans = 0
for l in range(n):
r = st.find_zero(l)
if r != -1:
ans = max(ans, r - l + 1)
v = nums[l]
sign = 1 if v % 2 else -1
dq = pos[v]
dq.popleft()
nxt = dq[0] if dq else n
st.range_add(l, nxt - 1, -sign)
return ansimport java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
/**
* 3721. Longest Balanced Subarray II
* Time: O(n log n)
* Space: O(n)
*/
class Solution {
public int longestBalancedSubarray(int[] nums) {
int n = nums.length;
Map<Integer, Deque<Integer>> pos = new HashMap<>();
for (int i = 0; i < n; i++) {
pos.computeIfAbsent(nums[i], k -> new ArrayDeque<>()).add(i);
}
SegmentTree st = new SegmentTree(n);
for (Map.Entry<Integer, Deque<Integer>> e : pos.entrySet()) {
int v = e.getKey();
int sign = (v % 2 == 1) ? 1 : -1;
st.rangeAdd(e.getValue().peekFirst(), n - 1, sign);
}
int ans = 0;
for (int l = 0; l < n; l++) {
int r = st.findZero(l);
if (r != -1) {
ans = Math.max(ans, r - l + 1);
}
int v = nums[l];
int sign = (v % 2 == 1) ? 1 : -1;
Deque<Integer> dq = pos.get(v);
dq.pollFirst();
int nxt = dq.isEmpty() ? n : dq.peekFirst();
st.rangeAdd(l, nxt - 1, -sign);
}
return ans;
}
}
class SegmentTree {
int n;
int[] minv, maxv, lazy;
SegmentTree(int n) {
this.n = n;
minv = new int[4 * n];
maxv = new int[4 * n];
lazy = new int[4 * n];
}
void push(int idx) {
if (lazy[idx] != 0) {
for (int child : new int[]{idx * 2, idx * 2 + 1}) {
minv[child] += lazy[idx];
maxv[child] += lazy[idx];
lazy[child] += lazy[idx];
}
lazy[idx] = 0;
}
}
void pull(int idx) {
minv[idx] = Math.min(minv[idx * 2], minv[idx * 2 + 1]);
maxv[idx] = Math.max(maxv[idx * 2], maxv[idx * 2 + 1]);
}
void rangeAdd(int l, int r, int val) {
rangeAdd(l, r, val, 1, 0, n - 1);
}
void rangeAdd(int l, int r, int val, int idx, int nl, int nr) {
if (r < nl || nr < l) return;
if (l <= nl && nr <= r) {
minv[idx] += val;
maxv[idx] += val;
lazy[idx] += val;
return;
}
push(idx);
int mid = (nl + nr) / 2;
rangeAdd(l, r, val, idx * 2, nl, mid);
rangeAdd(l, r, val, idx * 2 + 1, mid + 1, nr);
pull(idx);
}
int findZero(int ql) {
return findZero(ql, 1, 0, n - 1);
}
int findZero(int ql, int idx, int nl, int nr) {
if (nr < ql || minv[idx] > 0 || maxv[idx] < 0) return -1;
if (nl == nr) return nl;
push(idx);
int mid = (nl + nr) / 2;
int right = findZero(ql, idx * 2 + 1, mid + 1, nr);
if (right != -1) return right;
return findZero(ql, idx * 2, nl, mid);
}
}