3501. 操作后最大活跃区段数 II
给你一个长度为 n
的二进制字符串 s
,其中:
'1'
表示一个 活跃 区域。'0'
表示一个 非活跃 区域。你最多可以进行一次 操作 来最大化 s
中活跃区间的数量。在一次操作中,你可以:
'0'
包围的连续 '1'
区域转换为全 '0'
。'1'
包围的连续 '0'
区域转换为全 '1'
。此外,你还有一个 二维数组 queries
,其中 queries[i] = [li, ri]
表示子字符串 s[li...ri]
。
对于每个查询,确定在对子字符串 s[li...ri]
进行最优交换后,字符串 s
中 可能的最大 活跃区间数。
返回一个数组 answer
,其中 answer[i]
是 queries[i]
的结果。
注意
s[li...ri]
处理时,将其看作是在两端都加上一个 '1'
后的字符串,形成 t = '1' + s[li...ri] + '1'
。这些额外的 '1'
不会对最终的活跃区间数有贡献。
示例 1:
输入: s = "01", queries = [[0,1]]
输出: [1]
解释:
因为没有被 '0'
包围的 '1'
区域,所以没有有效的操作可以进行。最大活跃区间数是 1。
示例 2:
输入: s = "0100", queries = [[0,3],[0,2],[1,3],[2,3]]
输出: [4,3,1,1]
解释:
查询 [0, 3]
→ 子字符串 "0100"
→ 变为 "101001"
选择 "0100"
,"0100"
→ "0000"
→ "1111"
。
最终字符串(去掉添加的 '1'
)为 "1111"
。最大活跃区间数为 4。
查询 [0, 2]
→ 子字符串 "010"
→ 变为 "10101"
选择 "010"
,"010"
→ "000"
→ "111"
。
最终字符串(去掉添加的 '1'
)为 "1110"
。最大活跃区间数为 3。
查询 [1, 3]
→ 子字符串 "100"
→ 变为 "11001"
因为没有被 '0'
包围的 '1'
区域,所以没有有效的操作可以进行。最大活跃区间数为 1。
查询 [2, 3]
→ 子字符串 "00"
→ 变为 "1001"
因为没有被 '0'
包围的 '1'
区域,所以没有有效的操作可以进行。最大活跃区间数为 1。
示例 3:
输入: s = "1000100", queries = [[1,5],[0,6],[0,4]]
输出: [6,7,2]
解释:
查询 [1, 5]
→ 子字符串 "00010"
→ 变为 "1000101"
选择 "00010"
,"00010"
→ "00000"
→ "11111"
。
最终字符串(去掉添加的 '1'
)为 "1111110"
。最大活跃区间数为 6。
查询 [0, 6]
→ 子字符串 "1000100"
→ 变为 "110001001"
选择 "000100"
,"000100"
→ "000000"
→ "111111"
。
最终字符串(去掉添加的 '1'
)为 "1111111"
。最大活跃区间数为 7。
查询 [0, 4]
→ 子字符串 "10001"
→ 变为 "1100011"
因为没有被 '0'
包围的 '1'
区域,所以没有有效的操作可以进行。最大活跃区间数为 2。
示例 4:
输入: s = "01010", queries = [[0,3],[1,4],[1,3]]
输出: [4,4,2]
解释:
查询 [0, 3]
→ 子字符串 "0101"
→ 变为 "101011"
选择 "010"
,"010"
→ "000"
→ "111"
。
最终字符串(去掉添加的 '1'
)为 "11110"
。最大活跃区间数为 4。
查询 [1, 4]
→ 子字符串 "1010"
→ 变为 "110101"
选择 "010"
,"010"
→ "000"
→ "111"
。
最终字符串(去掉添加的 '1'
)为 "01111"
。最大活跃区间数为 4。
查询 [1, 3]
→ 子字符串 "101"
→ 变为 "11011"
因为没有被 '0'
包围的 '1'
区域,所以没有有效的操作可以进行。最大活跃区间数为 2。
提示:
1 <= n == s.length <= 105
1 <= queries.length <= 105
s[i]
只有 '0'
或 '1'
。queries[i] = [li, ri]
0 <= li <= ri < n
原站题解
golang 解法, 执行用时: 170 ms, 内存消耗: 23 MB, 提交时间: 2025-04-03 10:06:35
type pair struct{ l, r int } // 左闭右开 type ST [][]int func newST(a []pair) ST { n := len(a) - 1 sz := bits.Len(uint(n)) st := make(ST, n) for i, p := range a[:n] { st[i] = make([]int, sz) st[i][0] = p.r - p.l + a[i+1].r - a[i+1].l } for j := 1; j < sz; j++ { for i := 0; i+1<<j <= n; i++ { st[i][j] = max(st[i][j-1], st[i+1<<(j-1)][j-1]) } } return st } // 查询区间最大值,[l,r) 左闭右开 func (st ST) query(l, r int) int { if l >= r { return 0 } k := bits.Len(uint(r-l)) - 1 return max(st[l][k], st[r-1<<k][k]) } func maxActiveSectionsAfterTrade(s string, queries [][]int) []int { n := len(s) total1 := 0 // 统计连续 0 段对应的区间(左闭右开) a := []pair{{-1, -1}} // 哨兵 start := 0 for i := range n { if i == n-1 || s[i] != s[i+1] { if s[i] == '1' { total1 += i - start + 1 } else { a = append(a, pair{start, i + 1}) // 左闭右开 } start = i + 1 } } a = append(a, pair{n + 1, n + 1}) // 哨兵 merge := func(x, y int) int { if x > 0 && y > 0 { return x + y } return 0 } st := newST(a) m := len(a) ans := make([]int, len(queries)) for qi, q := range queries { ql, qr := q[0], q[1]+1 // 左闭右开 i := sort.Search(m, func(i int) bool { return a[i].l >= ql }) j := sort.Search(m, func(i int) bool { return a[i].r > qr }) - 1 mx := 0 if i <= j { // [ql,qr) 中有完整的区间 mx = max( st.query(i, j), // 相邻完整区间的长度之和的最大值 merge(a[i-1].r-ql, a[i].r-a[i].l), // 残缺区间 i-1 + 完整区间 i merge(qr-a[j+1].l, a[j].r-a[j].l), // 残缺区间 j+1 + 完整区间 j ) } else if i == j+1 { // [ql,qr) 中有两个相邻的残缺区间 mx = merge(a[i-1].r-ql, qr-a[j+1].l) // 残缺区间 i-1 + 残缺区间 j+1 } ans[qi] = total1 + mx } return ans }
python3 解法, 执行用时: 1234 ms, 内存消耗: 64.5 MB, 提交时间: 2025-04-03 10:06:17
class SparseTable: def __init__(self, a: List[Tuple[int, int]]): n = len(a) - 1 m = n.bit_length() st = [[r1 - l1 + r2 - l2] + [0] * (m - 1) for (l1, r1), (l2, r2) in pairwise(a)] for j in range(1, m): for i in range(n - (1 << j) + 1): st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]) self.st = st def query(self, l: int, r: int) -> int: if l >= r: return 0 k = (r - l).bit_length() - 1 return max(self.st[l][k], self.st[r - (1 << k)][k]) class Solution: def maxActiveSectionsAfterTrade(self, s: str, queries: List[List[int]]) -> List[int]: n = len(s) total1 = 0 belong = [0] * n # 每个 0 所属的区间下标,每个 1 右边最近的 0 区间下标 a = [(-1, -1)] start = 0 for i, b in enumerate(s): belong[i] = len(a) # 标记 if i == n - 1 or b != s[i + 1]: if b == '1': total1 += i - start + 1 else: a.append((start, i + 1)) start = i + 1 a.append((n + 1, n + 1)) def merge(x: int, y: int) -> int: return x + y if x > 0 and y > 0 else 0 st = SparseTable(a) ans = [] for ql, qr in queries: i = belong[ql] if ql and s[ql] == '0' == s[ql - 1]: i += 1 # i 在残缺区间中 j = belong[qr] - 1 if qr + 1 < n and s[qr] == '0' != s[qr + 1]: j += 1 # j 刚好在完整区间的右端点,无需减一 qr += 1 mx = 0 if i <= j: mx = max( st.query(i, j), merge(a[i - 1][1] - ql, a[i][1] - a[i][0]), merge(qr - a[j + 1][0], a[j][1] - a[j][0]), ) elif i == j + 1: mx = merge(a[i - 1][1] - ql, qr - a[j + 1][0]) ans.append(total1 + mx) return ans
java 解法, 执行用时: 62 ms, 内存消耗: 104.5 MB, 提交时间: 2025-04-03 10:05:59
class Solution { private record Pair(int l, int r) { } private static class SparseTable { private final int[][] st; SparseTable(List<Pair> a) { int n = a.size() - 1; int sz = 32 - Integer.numberOfLeadingZeros(n); st = new int[n][sz]; for (int i = 0; i < n; i++) { st[i][0] = a.get(i).r - a.get(i).l + a.get(i + 1).r - a.get(i + 1).l; } for (int j = 1; j < sz; j++) { for (int i = 0; i + (1 << j) <= n; i++) { st[i][j] = Math.max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); } } } int query(int l, int r) { if (l >= r) { return 0; } int k = 32 - Integer.numberOfLeadingZeros(r - l) - 1; return Math.max(st[l][k], st[r - (1 << k)][k]); } } public List<Integer> maxActiveSectionsAfterTrade(String S, int[][] queries) { char[] s = S.toCharArray(); int n = s.length; int total1 = 0; int[] belong = new int[n]; // 每个 0 所属的区间下标,每个 1 右边最近的 0 区间下标 List<Pair> a = new ArrayList<>(); a.add(new Pair(-1, -1)); int start = 0; for (int i = 0; i < n; i++) { belong[i] = a.size(); // 标记 if (i == n - 1 || s[i] != s[i + 1]) { if (s[i] == '1') { total1 += i - start + 1; } else { a.add(new Pair(start, i + 1)); } start = i + 1; } } a.add(new Pair(n + 1, n + 1)); SparseTable st = new SparseTable(a); List<Integer> ans = new ArrayList<>(queries.length); for (int[] query : queries) { int ql = query[0]; int qr = query[1]; int i = belong[ql]; if (ql > 0 && s[ql] == '0' && s[ql - 1] == '0') { i++; // i 在残缺区间中 } int j = belong[qr] - 1; if (qr + 1 < n && s[qr] == '0' && s[qr + 1] == '1') { j++; // j 刚好在完整区间的右端点,无需减一 } qr++; int mx = 0; if (i <= j) { int full = st.query(i, j); int sl = merge(a.get(i - 1).r - ql, a.get(i).r - a.get(i).l); int sr = merge(qr - a.get(j + 1).l, a.get(j).r - a.get(j).l); mx = Math.max(full, Math.max(sl, sr)); } else if (i == j + 1) { mx = merge(a.get(i - 1).r - ql, qr - a.get(j + 1).l); } ans.add(total1 + mx); } return ans; } private int merge(int x, int y) { return x > 0 && y > 0 ? x + y : 0; } }
cpp 解法, 执行用时: 197 ms, 内存消耗: 262 MB, 提交时间: 2025-04-03 10:05:32
struct Pair { int l, r; }; // 左闭右开 class SparseTable { vector<vector<int>> st; public: SparseTable(vector<Pair>& a) { int n = a.size() - 1; int sz = bit_width(unsigned(n)); st.resize(n, vector<int>(sz)); for (int i = 0; i < n; i++) { st[i][0] = a[i].r - a[i].l + a[i + 1].r - a[i + 1].l; } for (int j = 1; j < sz; j++) { for (int i = 0; i + (1 << j) <= n; i++) { st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); } } } // 查询区间最大值,[l,r) 左闭右开 int query(int l, int r) const { if (l >= r) { return 0; } int k = bit_width(unsigned(r - l)) - 1; return max(st[l][k], st[r - (1 << k)][k]); } }; class Solution { public: vector<int> maxActiveSectionsAfterTrade(string s, vector<vector<int>>& queries) { int n = s.size(); int total1 = 0; vector<Pair> a = {{-1, -1}}; // 哨兵 int start = 0; for (int i = 0; i < n; i++) { if (i == n - 1 || s[i] != s[i + 1]) { if (s[i] == '1') { total1 += i - start + 1; } else { a.emplace_back(start, i + 1); // 左闭右开 } start = i + 1; } } a.emplace_back(n + 1, n + 1); // 哨兵 auto merge = [](int x, int y) { return x > 0 && y > 0 ? x + y : 0; }; SparseTable st(a); vector<int> ans(queries.size()); for (int qi = 0; qi < queries.size(); qi++) { int ql = queries[qi][0], qr = queries[qi][1] + 1; // 左闭右开 int i = ranges::lower_bound(a, ql, {}, &Pair::l) - a.begin(); int j = ranges::upper_bound(a, qr, {}, &Pair::r) - a.begin() - 1; int mx = 0; if (i <= j) { // [ql,qr) 中有完整的区间 mx = max({ st.query(i, j), // 相邻完整区间的长度之和的最大值 merge(a[i - 1].r - ql, a[i].r - a[i].l), // 残缺区间 i-1 + 完整区间 i merge(qr - a[j + 1].l, a[j].r - a[j].l), // 残缺区间 j+1 + 完整区间 j }); } else if (i == j + 1) { // [ql,qr) 中有两个相邻的残缺区间 mx = merge(a[i - 1].r - ql, qr - a[j + 1].l); // 残缺区间 i-1 + 残缺区间 j+1 } ans[qi] = total1 + mx; } return ans; } };