Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
You are given a string s of length n consisting only of the characters 'A' and 'B'.
You are also given a 2D integer array queries of length q, where each queries[i] is one of the following:
[1, j]: Flip the character at index j of s i.e. 'A' changes to 'B' (and vice versa). This operation mutates s and affects subsequent queries.[2, l, r]: Compute the minimum number of character deletions required to make the substring s[l..r] alternating. This operation does not modify s; the length of s remains n.A substring is alternating if no two adjacent characters are equal. A substring of length 1 is always alternating.
Return an integer array answer, where answer[i] is the result of the ith query of type [2, l, r].
Example 1:
Input: s = "ABA", queries = [[2,1,2],[1,1],[2,0,2]]
Output: [0,2]
Explanation:
i |
queries[i] |
j |
l |
r |
s before query |
s[l..r] |
Result | Answer |
|---|---|---|---|---|---|---|---|---|
| 0 | [2, 1, 2] | - | 1 | 2 | "ABA" |
"BA" |
Already alternating | 0 |
| 1 | [1, 1] | 1 | - | - | "ABA" |
- | Flip s[1] from 'B' to 'A' |
- |
| 2 | [2, 0, 2] | - | 0 | 2 | "AAA" |
"AAA" |
Delete any two 'A's to get "A" |
2 |
Thus, the answer is [0, 2].
Example 2:
Input: s = "ABB", queries = [[2,0,2],[1,2],[2,0,2]]
Output: [1,0]
Explanation:
i |
queries[i] |
j |
l |
r |
s before query |
s[l..r] |
Result | Answer |
|---|---|---|---|---|---|---|---|---|
| 0 | [2, 0, 2] | - | 0 | 2 | "ABB" |
"ABB" |
Delete one 'B' to get "AB" |
1 |
| 1 | [1, 2] | 2 | - | - | "ABB" |
- | Flip s[2] from 'B' to 'A' |
- |
| 2 | [2, 0, 2] | - | 0 | 2 | "ABA" |
"ABA" |
Already alternating | 0 |
Thus, the answer is [1, 0].
Example 3:
Input: s = "BABA", queries = [[2,0,3],[1,1],[2,1,3]]
Output: [0,1]
Explanation:
i |
queries[i] |
j |
l |
r |
s before query |
s[l..r] |
Result | Answer |
|---|---|---|---|---|---|---|---|---|
| 0 | [2, 0, 3] | - | 0 | 3 | "BABA" |
"BABA" |
Already alternating | 0 |
| 1 | [1, 1] | 1 | - | - | "BABA" |
- | Flip s[1] from 'A' to 'B' |
- |
| 2 | [2, 1, 3] | - | 1 | 3 | "BBBA" |
"BBA" |
Delete one 'B' to get "BA" |
1 |
Thus, the answer is [0, 1].
Constraints:
1 <= n == s.length <= 105s[i] is either 'A' or 'B'.1 <= q == queries.length <= 105queries[i].length == 2 or 3
queries[i] == [1, j] or,queries[i] == [2, l, r]0 <= j <= n - 10 <= l <= r <= n - 1Problem summary: You are given a string s of length n consisting only of the characters 'A' and 'B'. You are also given a 2D integer array queries of length q, where each queries[i] is one of the following: [1, j]: Flip the character at index j of s i.e. 'A' changes to 'B' (and vice versa). This operation mutates s and affects subsequent queries. [2, l, r]: Compute the minimum number of character deletions required to make the substring s[l..r] alternating. This operation does not modify s; the length of s remains n. A substring is alternating if no two adjacent characters are equal. A substring of length 1 is always alternating. Return an integer array answer, where answer[i] is the result of the ith query of type [2, l, r].
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Segment Tree
"ABA" [[2,1,2],[1,1],[2,0,2]]
"ABB" [[2,0,2],[1,2],[2,0,2]]
"BABA" [[2,0,3],[1,1],[2,1,3]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3777: Minimum Deletions to Make Alternating Substring
class BinaryIndexedTree {
int n;
int[] c;
BinaryIndexedTree(int n) {
this.n = n;
this.c = new int[n + 1];
}
void update(int x, int delta) {
while (x <= n) {
c[x] += delta;
x += x & -x;
}
}
int query(int x) {
int s = 0;
while (x > 0) {
s += c[x];
x -= x & -x;
}
return s;
}
}
class Solution {
public int[] minDeletions(String s, int[][] queries) {
int n = s.length();
int[] nums = new int[n];
BinaryIndexedTree bit = new BinaryIndexedTree(n);
for (int i = 1; i < n; i++) {
nums[i] = (s.charAt(i) == s.charAt(i - 1)) ? 1 : 0;
if (nums[i] == 1) {
bit.update(i + 1, 1);
}
}
int cnt = 0;
for (int[] q : queries) {
if (q[0] == 2) {
cnt++;
}
}
int[] ans = new int[cnt];
int idx = 0;
for (int[] q : queries) {
if (q[0] == 1) {
int j = q[1];
int delta = (nums[j] ^ 1) - nums[j];
nums[j] ^= 1;
bit.update(j + 1, delta);
if (j + 1 < n) {
delta = (nums[j + 1] ^ 1) - nums[j + 1];
nums[j + 1] ^= 1;
bit.update(j + 2, delta);
}
} else {
int l = q[1];
int r = q[2];
ans[idx++] = bit.query(r + 1) - bit.query(l + 1);
}
}
return ans;
}
}
// Accepted solution for LeetCode #3777: Minimum Deletions to Make Alternating Substring
type binaryIndexedTree struct {
n int
c []int
}
func newBinaryIndexedTree(n int) *binaryIndexedTree {
return &binaryIndexedTree{
n: n,
c: make([]int, n+1),
}
}
func (bit *binaryIndexedTree) update(x, delta int) {
for x <= bit.n {
bit.c[x] += delta
x += x & -x
}
}
func (bit *binaryIndexedTree) query(x int) int {
s := 0
for x > 0 {
s += bit.c[x]
x -= x & -x
}
return s
}
func minDeletions(s string, queries [][]int) []int {
n := len(s)
nums := make([]int, n)
bit := newBinaryIndexedTree(n)
for i := 1; i < n; i++ {
if s[i] == s[i-1] {
nums[i] = 1
bit.update(i+1, 1)
}
}
ans := make([]int, 0)
for _, q := range queries {
if q[0] == 1 {
j := q[1]
delta := (nums[j] ^ 1 - nums[j])
nums[j] ^= 1
bit.update(j+1, delta)
if j+1 < n {
delta = (nums[j+1] ^ 1 - nums[j+1])
nums[j+1] ^= 1
bit.update(j+2, delta)
}
} else {
l, r := q[1], q[2]
ans = append(ans, bit.query(r+1)-bit.query(l+1))
}
}
return ans
}
# Accepted solution for LeetCode #3777: Minimum Deletions to Make Alternating Substring
class BinaryIndexedTree:
__slots__ = "n", "c"
def __init__(self, n: int):
self.n = n
self.c = [0] * (n + 1)
def update(self, x: int, delta: int) -> None:
while x <= self.n:
self.c[x] += delta
x += x & -x
def query(self, x: int) -> int:
s = 0
while x:
s += self.c[x]
x -= x & -x
return s
class Solution:
def minDeletions(self, s: str, queries: List[List[int]]) -> List[int]:
n = len(s)
nums = [0] * n
bit = BinaryIndexedTree(n)
for i in range(1, n):
nums[i] = int(s[i] == s[i - 1])
if nums[i]:
bit.update(i + 1, 1)
ans = []
for q in queries:
if q[0] == 1:
j = q[1]
delta = (nums[j] ^ 1) - nums[j]
nums[j] ^= 1
bit.update(j + 1, delta)
if j + 1 < n:
delta = (nums[j + 1] ^ 1) - nums[j + 1]
nums[j + 1] ^= 1
bit.update(j + 2, delta)
else:
_, l, r = q
ans.append(bit.query(r + 1) - bit.query(l + 1))
return ans
// Accepted solution for LeetCode #3777: Minimum Deletions to Make Alternating Substring
// Rust example auto-generated from java reference.
// Replace the signature and local types with the exact LeetCode harness for this problem.
impl Solution {
pub fn rust_example() {
// Port the logic from the reference block below.
}
}
// Reference (java):
// // Accepted solution for LeetCode #3777: Minimum Deletions to Make Alternating Substring
// class BinaryIndexedTree {
// int n;
// int[] c;
//
// BinaryIndexedTree(int n) {
// this.n = n;
// this.c = new int[n + 1];
// }
//
// void update(int x, int delta) {
// while (x <= n) {
// c[x] += delta;
// x += x & -x;
// }
// }
//
// int query(int x) {
// int s = 0;
// while (x > 0) {
// s += c[x];
// x -= x & -x;
// }
// return s;
// }
// }
//
// class Solution {
// public int[] minDeletions(String s, int[][] queries) {
// int n = s.length();
// int[] nums = new int[n];
// BinaryIndexedTree bit = new BinaryIndexedTree(n);
//
// for (int i = 1; i < n; i++) {
// nums[i] = (s.charAt(i) == s.charAt(i - 1)) ? 1 : 0;
// if (nums[i] == 1) {
// bit.update(i + 1, 1);
// }
// }
//
// int cnt = 0;
// for (int[] q : queries) {
// if (q[0] == 2) {
// cnt++;
// }
// }
//
// int[] ans = new int[cnt];
// int idx = 0;
//
// for (int[] q : queries) {
// if (q[0] == 1) {
// int j = q[1];
//
// int delta = (nums[j] ^ 1) - nums[j];
// nums[j] ^= 1;
// bit.update(j + 1, delta);
//
// if (j + 1 < n) {
// delta = (nums[j + 1] ^ 1) - nums[j + 1];
// nums[j + 1] ^= 1;
// bit.update(j + 2, delta);
// }
// } else {
// int l = q[1];
// int r = q[2];
// ans[idx++] = bit.query(r + 1) - bit.query(l + 1);
// }
// }
// return ans;
// }
// }
// Accepted solution for LeetCode #3777: Minimum Deletions to Make Alternating Substring
class BinaryIndexedTree {
n: number;
c: number[];
constructor(n: number) {
this.n = n;
this.c = Array(n + 1).fill(0);
}
update(x: number, delta: number): void {
while (x <= this.n) {
this.c[x] += delta;
x += x & -x;
}
}
query(x: number): number {
let s = 0;
while (x > 0) {
s += this.c[x];
x -= x & -x;
}
return s;
}
}
function minDeletions(s: string, queries: number[][]): number[] {
const n = s.length;
const nums: number[] = Array(n).fill(0);
const bit = new BinaryIndexedTree(n);
for (let i = 1; i < n; i++) {
if (s[i] === s[i - 1]) {
nums[i] = 1;
bit.update(i + 1, 1);
}
}
const ans: number[] = [];
for (const q of queries) {
if (q[0] === 1) {
const j = q[1];
let delta = (nums[j] ^ 1) - nums[j];
nums[j] ^= 1;
bit.update(j + 1, delta);
if (j + 1 < n) {
delta = (nums[j + 1] ^ 1) - nums[j + 1];
nums[j + 1] ^= 1;
bit.update(j + 2, delta);
}
} else {
const l = q[1],
r = q[2];
ans.push(bit.query(r + 1) - bit.query(l + 1));
}
}
return ans;
}
Use this to step through a reusable interview workflow for this problem.
For each of q queries, scan the entire range to compute the aggregate (sum, min, max). Each range scan takes up to O(n) for a full-array query. With q queries: O(n × q) total. Point updates are O(1) but queries dominate.
Building the tree is O(n). Each query or update traverses O(log n) nodes (tree height). For q queries: O(n + q log n) total. Space is O(4n) ≈ O(n) for the tree array. Lazy propagation adds O(1) per node for deferred updates.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.