Facebook Pixel

3777. Minimum Deletions to Make Alternating Substring

HardSegment TreeString
LeetCode ↗

Problem Description

You are given a string s of length n that consists only of the characters 'A' and 'B'.

You are also given a 2D integer array queries of length q. Each queries[i] is one of the following two types:

  • [1, j]: Flip the character at index j of s. This means 'A' becomes 'B' and 'B' becomes 'A'. This operation changes s and therefore affects all subsequent queries.
  • [2, l, r]: Compute the minimum number of character deletions needed to make the substring s[l..r] alternating. This operation does not modify s; the length of s stays n.

A substring is alternating if no two adjacent characters are equal. A substring of length 1 is always considered alternating.

Return an integer array answer, where answer[i] is the result of the i-th query of type [2, l, r].

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

How We Pick the Algorithm

Why Ordered Set / Fenwick / Segment Tree?

This problem maps to Ordered Set / Fenwick / Segment Tree through a short path in the full flowchart.

Rangequeries orupdates?yesPointupdates?yesOrdered Set /Fenwick /Segment Tree

Flipping characters and querying range sums of adjacent-equal pairs requires a segment tree for efficient updates and queries.

Open in Flowchart

Intuition

The key observation is to think about what "deletions to make a substring alternating" really means. A substring is alternating when no two adjacent characters are equal. So whenever we find a pair of adjacent equal characters, we must delete one of them to break that pair. In fact, the minimum number of deletions required is exactly equal to the number of positions where two adjacent characters are equal.

This insight lets us reframe the problem. Instead of looking at the characters directly, we look at the boundaries between adjacent characters. For each index i (with 1 <= i < n), we define a value nums[i] that records whether s[i] equals s[i-1]:

  • If s[i] == s[i-1], then nums[i] = 1 (this is a "bad" boundary that needs a deletion).
  • Otherwise, nums[i] = 0.

With this transformation, the answer to a query [2, l, r] becomes the sum of nums over the interval [l+1, r]. We start from l+1 because the boundary at index l compares s[l] with s[l-1], which lies outside the substring and should not be counted.

Now consider a flip query [1, j]. Flipping s[j] only changes its relationship with its immediate neighbors. Specifically, it affects the boundary between s[j-1] and s[j] (recorded at nums[j]) and the boundary between s[j] and s[j+1] (recorded at nums[j+1]). Since a flip turns an equal pair into an unequal pair and vice versa, each affected nums value simply toggles between 0 and 1. No other positions are touched.

So we are left with two operations: point updates (toggling at most two nums values per flip) and range sum queries (summing nums over [l+1, r]). This combination of fast updates and fast range sums is exactly what a Binary Indexed Tree (Fenwick Tree) handles efficiently, giving us O(log n) time per operation.

Pattern Learn more about Segment Tree patterns.

Solution Approach

We use a Binary Indexed Tree (Fenwick Tree) to maintain the prefix sum of the nums array, which lets us perform both point updates and range sum queries in O(log n) time.

Step 1: Build the nums array.

We convert the string s into an array nums of length n, where:

  • nums[0] = 0 (there is no character before index 0).
  • For 1 <= i < n, nums[i] = 1 if s[i] == s[i-1], otherwise nums[i] = 0.

Each nums[i] indicates whether there is a pair of adjacent equal characters ending at index i.

Step 2: Initialize the Binary Indexed Tree.

We create a BinaryIndexedTree of size n. While building nums, whenever nums[i] == 1, we call bit.update(i + 1, 1) to add this value to the tree. Note that the tree uses 1-based indexing, so position i in nums maps to position i + 1 in the tree.

The tree supports two core operations:

  • update(x, delta): adds delta to position x and propagates the change upward using x += x & -x.
  • query(x): returns the prefix sum of the first x positions by walking down with x -= x & -x.

Step 3: Process the queries.

We iterate through each query and handle the two types:

  • Flip query [1, j]: A flip only affects the boundaries at nums[j] and nums[j+1]. For each affected position, we compute the change delta = (nums[pos] ^ 1) - nums[pos], which is +1 if the value goes from 0 to 1 and -1 if it goes from 1 to 0. We then toggle the value with nums[pos] ^= 1 and apply the change to the tree via bit.update. We handle nums[j] directly, and only handle nums[j+1] when j + 1 < n to stay within bounds.

  • Compute query [2, l, r]: The answer is the sum of nums over the interval [l+1, r]. Using prefix sums, this equals bit.query(r + 1) - bit.query(l + 1). Here bit.query(r + 1) gives the sum of nums[0..r] and bit.query(l + 1) gives the sum of nums[0..l], so subtracting leaves the sum over nums[l+1..r], exactly the boundaries inside the substring. We append this result to ans.

Step 4: Return the result.

After processing all queries, we return the ans array containing the answers to all [2, l, r] queries in order.

Complexity Analysis:

  • Time complexity: O((n + q) * log n), where n is the length of s and q is the number of queries. Building the tree takes O(n log n), and each query is handled in O(log n).
  • Space complexity: O(n) for the nums array and the Binary Indexed Tree.

Example Walkthrough

Let's trace through a small example to see the solution approach in action.

Input:

  • s = "ABBA" (so n = 4)
  • queries = [[2, 0, 3], [1, 1], [2, 0, 3]]

Step 1: Build the nums array.

We compare each character with its predecessor. Recall nums[i] = 1 when s[i] == s[i-1], else 0. By definition nums[0] = 0.

Index is[i]s[i-1]Equal?nums[i]
0A0
1BANo0
2BBYes1
3ABNo0

So nums = [0, 0, 1, 0]. The single "bad boundary" sits at index 2, where "BB" are adjacent and equal.


Step 2: Initialize the Binary Indexed Tree.

We create a BIT of size 4 (1-based). For every nums[i] == 1, we call bit.update(i + 1, 1).

  • Only nums[2] == 1, so we call bit.update(3, 1).

The BIT now logically holds the array [0, 0, 1, 0] and can answer prefix sums.


Step 3: Process the queries.

Query 1 — [2, 0, 3] (Compute):

We want the minimum deletions to make s[0..3] = "ABBA" alternating. The answer is the sum of nums over [l+1, r] = [1, 3].

answer = bit.query(r + 1) - bit.query(l + 1)
       = bit.query(4)     - bit.query(1)
       = (nums[0..3] sum) - (nums[0..0] sum)
       = (0+0+1+0)        - (0)
       = 1

So we need 1 deletion. Intuitively, "ABBA" has one adjacent equal pair ("BB"); deleting one B gives "ABA", which is alternating. ✅ We append 1 to ans.

ans = [1]


Query 2 — [1, 1] (Flip):

We flip s[1] from B to A. The string becomes s = "AABA". A flip at index j = 1 only affects two boundaries: nums[j] = nums[1] and nums[j+1] = nums[2].

Affecting nums[1]:

pos = 1
delta = (nums[1] ^ 1) - nums[1] = (0 ^ 1) - 0 = 1 - 0 = +1
nums[1] ^= 1   ->  nums[1] = 1
bit.update(pos + 1, delta) -> bit.update(2, +1)

Now s[0]='A', s[1]='A' are equal, so the new boundary is "bad" — consistent with nums[1] = 1.

Affecting nums[2] (since j+1 = 2 < n):

pos = 2
delta = (nums[2] ^ 1) - nums[2] = (1 ^ 1) - 1 = 0 - 1 = -1
nums[2] ^= 1   ->  nums[2] = 0
bit.update(pos + 1, delta) -> bit.update(3, -1)

Now s[1]='A', s[2]='B' are unequal, so that boundary is no longer "bad" — consistent with nums[2] = 0.

After the flip, nums = [0, 1, 0, 0] and the BIT reflects the array [0, 1, 0, 0]. Notice we only touched two positions, never re-scanning the whole string.


Query 3 — [2, 0, 3] (Compute):

Same range as before, but s is now "AABA". The answer is the sum of nums over [1, 3].

answer = bit.query(4) - bit.query(1)
       = (0+1+0+0)    - (0)
       = 1

So we need 1 deletion again. Checking "AABA": it has one adjacent equal pair ("AA"); deleting one A gives "ABA", which is alternating. ✅ We append 1 to ans.

ans = [1, 1]


Step 4: Return the result.

After all queries, we return:

ans = [1, 1]

Key takeaways from this walkthrough:

  1. The problem reduces to counting adjacent-equal boundaries inside a range, not the characters themselves.
  2. A range sum query on nums answers each compute query in O(log n).
  3. A flip is a localized event — it toggles at most two nums values, each handled by a single O(log n) BIT update, so the data structure never falls out of sync with the string.

Solution Implementation

1from typing import List
2
3
4class BinaryIndexedTree:
5    __slots__ = "n", "tree"
6
7    def __init__(self, n: int):
8        # Size of the tree (1-indexed internally)
9        self.n = n
10        # Storage array; index 0 is unused for convenience
11        self.tree = [0] * (n + 1)
12
13    def update(self, x: int, delta: int) -> None:
14        # Add `delta` at position x, propagating upward through parents
15        while x <= self.n:
16            self.tree[x] += delta
17            x += x & -x  # move to the next responsible node
18
19    def query(self, x: int) -> int:
20        # Prefix sum of all elements from index 1 to x
21        total = 0
22        while x > 0:
23            total += self.tree[x]
24            x -= x & -x  # move to the parent prefix block
25        return total
26
27
28class Solution:
29    def minDeletions(self, s: str, queries: List[List[int]]) -> List[int]:
30        n = len(s)
31        # nums[i] == 1 means s[i] equals s[i-1] (an adjacent duplicate pair)
32        nums = [0] * n
33        bit = BinaryIndexedTree(n)
34
35        # Build the initial Fenwick Tree from adjacent-pair flags
36        for i in range(1, n):
37            nums[i] = int(s[i] == s[i - 1])
38            if nums[i]:
39                # Store flag at 1-indexed position (i + 1)
40                bit.update(i + 1, 1)
41
42        ans: List[int] = []
43        for q in queries:
44            if q[0] == 1:
45                # Type 1: toggle the duplicate-pair flags around index j
46                j = q[1]
47
48                # Toggle nums[j] and reflect the change in the BIT
49                delta = (nums[j] ^ 1) - nums[j]
50                nums[j] ^= 1
51                bit.update(j + 1, delta)
52
53                # The change at j may also affect the pair flag at j + 1
54                if j + 1 < n:
55                    delta = (nums[j + 1] ^ 1) - nums[j + 1]
56                    nums[j + 1] ^= 1
57                    bit.update(j + 2, delta)
58            else:
59                # Type 2: count duplicate pairs within range [l, r]
60                # Equals minimum deletions to remove consecutive duplicates
61                _, l, r = q
62                ans.append(bit.query(r + 1) - bit.query(l + 1))
63
64        return ans
65
1/**
2 * A Binary Indexed Tree (Fenwick Tree) that supports point updates
3 * and prefix-sum queries in O(log n) time.
4 */
5class BinaryIndexedTree {
6    // Size of the underlying logical array.
7    private int size;
8    // Internal tree storage; indices are 1-based.
9    private int[] tree;
10
11    /**
12     * Constructs a Binary Indexed Tree for an array of the given length.
13     *
14     * @param size the number of elements the tree should support
15     */
16    BinaryIndexedTree(int size) {
17        this.size = size;
18        this.tree = new int[size + 1];
19    }
20
21    /**
22     * Adds {@code delta} to the element at position {@code index} (1-based).
23     *
24     * @param index the 1-based position to update
25     * @param delta the value to add at that position
26     */
27    void update(int index, int delta) {
28        // Walk upward, updating every responsible node by adding the lowbit.
29        while (index <= size) {
30            tree[index] += delta;
31            index += index & -index;
32        }
33    }
34
35    /**
36     * Returns the prefix sum of the first {@code index} elements (1-based).
37     *
38     * @param index the 1-based right boundary (inclusive) of the prefix
39     * @return the sum of elements in the range [1, index]
40     */
41    int query(int index) {
42        int sum = 0;
43        // Walk downward, accumulating partial sums by removing the lowbit.
44        while (index > 0) {
45            sum += tree[index];
46            index -= index & -index;
47        }
48        return sum;
49    }
50}
51
52class Solution {
53    /**
54     * Processes a list of queries over a binary string.
55     *
56     * <p>For each character at position {@code i} (i >= 1), {@code marks[i]} is 1
57     * when {@code s[i] == s[i - 1]} (i.e., this position forms an "equal-adjacent"
58     * pair with its predecessor). The Binary Indexed Tree stores these marks so
59     * that range counts of such pairs can be answered quickly.</p>
60     *
61     * <p>Query types:</p>
62     * <ul>
63     *   <li>{@code [1, j]} — toggle character at index {@code j}, refreshing the
64     *       affected adjacency marks at positions {@code j} and {@code j + 1}.</li>
65     *   <li>{@code [2, l, r]} — count the equal-adjacent pairs within [l, r],
66     *       which equals the minimum deletions needed to remove repeats.</li>
67     * </ul>
68     *
69     * @param s       the binary string
70     * @param queries the array of queries to process
71     * @return an array of answers, one per type-2 query, in order
72     */
73    public int[] minDeletions(String s, int[][] queries) {
74        int length = s.length();
75        // marks[i] == 1 means s[i] equals s[i - 1]; marks[0] is always 0.
76        int[] marks = new int[length];
77        BinaryIndexedTree bit = new BinaryIndexedTree(length);
78
79        // Initialize the adjacency marks and seed the tree accordingly.
80        for (int i = 1; i < length; i++) {
81            marks[i] = (s.charAt(i) == s.charAt(i - 1)) ? 1 : 0;
82            if (marks[i] == 1) {
83                bit.update(i + 1, 1);
84            }
85        }
86
87        // Pre-count how many type-2 queries exist to size the answer array.
88        int answerCount = 0;
89        for (int[] query : queries) {
90            if (query[0] == 2) {
91                answerCount++;
92            }
93        }
94
95        int[] answers = new int[answerCount];
96        int answerIndex = 0;
97
98        // Note: toggling a character does not change the actual mark values used
99        // in the original logic; the code flips marks and applies the delta so the
100        // tree stays consistent with the maintained marks array.
101        for (int[] query : queries) {
102            if (query[0] == 1) {
103                int position = query[1];
104
105                // Flip the mark at this position and reflect the change in the tree.
106                int delta = (marks[position] ^ 1) - marks[position];
107                marks[position] ^= 1;
108                bit.update(position + 1, delta);
109
110                // Flip the mark at the next position as it shares the adjacency.
111                if (position + 1 < length) {
112                    delta = (marks[position + 1] ^ 1) - marks[position + 1];
113                    marks[position + 1] ^= 1;
114                    bit.update(position + 2, delta);
115                }
116            } else {
117                int left = query[1];
118                int right = query[2];
119                // Count equal-adjacent pairs in (left, right] of the marks array.
120                answers[answerIndex++] = bit.query(right + 1) - bit.query(left + 1);
121            }
122        }
123        return answers;
124    }
125}
126
1class BinaryIndexedTree {
2public:
3    int size;                  // Total number of positions the tree manages
4    vector<int> tree;          // Internal Fenwick tree storage (1-indexed)
5
6    BinaryIndexedTree(int size)
7        : size(size)
8        , tree(size + 1, 0) {}
9
10    // Add `delta` to position `index` and propagate to affected nodes
11    void update(int index, int delta) {
12        while (index <= size) {
13            tree[index] += delta;
14            index += index & -index;   // Move to the next responsible node
15        }
16    }
17
18    // Return the prefix sum from position 1 up to `index` (inclusive)
19    int query(int index) {
20        int sum = 0;
21        while (index > 0) {
22            sum += tree[index];
23            index -= index & -index;   // Move to the parent node
24        }
25        return sum;
26    }
27};
28
29class Solution {
30public:
31    vector<int> minDeletions(string s, vector<vector<int>>& queries) {
32        int length = s.size();
33
34        // marks[i] == 1 means s[i] equals its previous character s[i-1].
35        // Each such adjacent equal pair requires exactly one deletion to fix.
36        vector<int> marks(length, 0);
37        BinaryIndexedTree bit(length);
38
39        // Initialize marks and the Fenwick tree based on adjacent equal pairs.
40        for (int i = 1; i < length; i++) {
41            marks[i] = (s[i] == s[i - 1]);
42            if (marks[i]) {
43                bit.update(i + 1, 1);  // Tree is 1-indexed, so store at i + 1
44            }
45        }
46
47        vector<int> answer;
48
49        for (auto& query : queries) {
50            if (query[0] == 1) {
51                // Type 1: toggle the character at position `pos`.
52                // Toggling affects the equality relation at `pos` (with pos-1)
53                // and at `pos + 1` (with pos), so update both marks.
54                int pos = query[1];
55
56                // Flip the mark at `pos` and reflect the change in the tree.
57                int delta = (marks[pos] ^ 1) - marks[pos];
58                marks[pos] ^= 1;
59                bit.update(pos + 1, delta);
60
61                // Flip the mark at `pos + 1` if it exists.
62                if (pos + 1 < length) {
63                    delta = (marks[pos + 1] ^ 1) - marks[pos + 1];
64                    marks[pos + 1] ^= 1;
65                    bit.update(pos + 2, delta);
66                }
67            } else {
68                // Type 2: count the deletions needed within range [left, right].
69                // This equals the number of marked positions in (left, right],
70                // which is the count of adjacent equal pairs in that substring.
71                int left = query[1];
72                int right = query[2];
73                answer.push_back(bit.query(right + 1) - bit.query(left + 1));
74            }
75        }
76        return answer;
77    }
78};
79
1// Size of the Binary Indexed Tree (Fenwick Tree)
2let treeSize: number;
3
4// 1-indexed array storing the Binary Indexed Tree
5let treeArray: number[];
6
7/**
8 * Adds `delta` to position `x` and propagates the change
9 * to all responsible nodes in the Fenwick Tree.
10 */
11function update(x: number, delta: number): void {
12    while (x <= treeSize) {
13        treeArray[x] += delta;
14        x += x & -x; // move to the next node that covers position x
15    }
16}
17
18/**
19 * Returns the prefix sum from index 1 up to index `x`.
20 */
21function query(x: number): number {
22    let sum = 0;
23    while (x > 0) {
24        sum += treeArray[x];
25        x -= x & -x; // move to the parent prefix node
26    }
27    return sum;
28}
29
30/**
31 * For each "count" query, returns the number of deletions required
32 * so that no two adjacent characters in s[l..r] are equal.
33 * A deletion is needed wherever s[i] === s[i - 1].
34 */
35function minDeletions(s: string, queries: number[][]): number[] {
36    const n = s.length;
37
38    // nums[i] = 1 means s[i] equals s[i - 1] (an adjacent duplicate pair)
39    const nums: number[] = Array(n).fill(0);
40
41    // Initialize the Fenwick Tree as a fresh global structure
42    treeSize = n;
43    treeArray = Array(n + 1).fill(0);
44
45    // Record every adjacent duplicate pair in the BIT
46    for (let i = 1; i < n; i++) {
47        if (s[i] === s[i - 1]) {
48            nums[i] = 1;
49            update(i + 1, 1);
50        }
51    }
52
53    const ans: number[] = [];
54
55    for (const q of queries) {
56        if (q[0] === 1) {
57            // Update query: flip the character at index j.
58            const j = q[1];
59
60            // Toggle the pair (j - 1, j) stored at nums[j]
61            let delta = (nums[j] ^ 1) - nums[j];
62            nums[j] ^= 1;
63            update(j + 1, delta);
64
65            // Toggle the pair (j, j + 1) stored at nums[j + 1], if it exists
66            if (j + 1 < n) {
67                delta = (nums[j + 1] ^ 1) - nums[j + 1];
68                nums[j + 1] ^= 1;
69                update(j + 2, delta);
70            }
71        } else {
72            // Count query: number of duplicate pairs within [l, r].
73            // Pairs are counted at their right index, so the valid range
74            // in BIT terms is (l + 1, r + 1].
75            const l = q[1];
76            const r = q[2];
77            ans.push(query(r + 1) - query(l + 1));
78        }
79    }
80
81    return ans;
82}
83

Time and Space Complexity

  • Time Complexity: O((n + q) × log n), where n is the length of the string s, and q is the number of queries.

    The analysis is as follows:

    1. Initialization phase: We iterate over the string s from index 1 to n - 1. For each index i, we compute nums[i] in O(1) time. Whenever nums[i] is 1, we call bit.update(i + 1, 1), which takes O(log n) time because the Binary Indexed Tree update operation traverses at most log n nodes (following x += x & -x). Hence, this phase costs O(n × log n).

    2. Query phase: For each of the q queries:

      • A type 1 query (update) performs at most two bit.update operations, each costing O(log n), for a total of O(log n) per query.
      • A type 2 query (range query) performs two bit.query operations, each costing O(log n) because the query traverses at most log n nodes (following x -= x & -x), for a total of O(log n) per query.

      Therefore, processing all queries costs O(q × log n).

    Combining both phases, the overall time complexity is O(n × log n + q × log n) = O((n + q) × log n).

  • Space Complexity: O(n), where n is the length of the string s.

    The analysis is as follows:

    1. The array nums uses O(n) space.
    2. The Binary Indexed Tree's internal array c has size n + 1, using O(n) space.
    3. The answer list ans stores at most q results, but this is typically considered part of the output rather than auxiliary space.

    Thus, the dominant auxiliary space usage is O(n).

Pattern Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Incorrectly Updating the Boundary Flags After a Flip

The most common mistake when handling a Flip query [1, j] is misunderstanding which nums entries actually change. Flipping s[j] does not change s[j] relative to itself — it changes how s[j] compares to its neighbors:

  • nums[j] encodes whether s[j] == s[j-1].
  • nums[j+1] encodes whether s[j+1] == s[j].

Both of these comparisons involve s[j], so flipping s[j] toggles both flags. A frequent error is to only update one of them (e.g., only nums[j]), which silently corrupts the tree.

Wrong:

# Only updates one boundary — leaves nums[j+1] stale!
j = q[1]
delta = (nums[j] ^ 1) - nums[j]
nums[j] ^= 1
bit.update(j + 1, delta)

Why a toggle works: When you flip s[j], equality with a fixed neighbor always inverts. If they were equal (nums == 1) they become different (nums == 0) and vice versa. So nums[pos] ^= 1 is always the correct transition — there is no need to re-read the actual characters.

Correct: Update both nums[j] and nums[j+1] (guarding the upper bound):

j = q[1]
for pos in (j, j + 1):
    if 0 < pos < n:          # nums[0] is never a valid pair flag
        delta = (nums[pos] ^ 1) - nums[pos]
        nums[pos] ^= 1
        bit.update(pos + 1, delta)

Note: nums[0] is always 0 by definition (no character precedes index 0). If j == 0, only nums[1] is meaningful — the loop above guards this with 0 < pos. The original code is also correct because nums[0] is never touched by a flip at j = 0 in a way that affects answers (since queries [2, l, r] use bit.query(l + 1), position 0’s flag is always excluded from any answer range), but the explicit 0 < pos guard makes the intent clearer and avoids edge-case confusion.


Pitfall 2: Off-by-One Errors in Index Mapping (0-based vs 1-based)

There are three coexisting index systems, and mixing them up is the leading source of bugs:

ConceptIndexing
String s / array nums0-based
Fenwick Tree positions1-based (nums[i] → tree position i+1)
Query range [l, r]0-based, inclusive

The range query must sum nums[l+1 .. r] (the internal boundaries of the substring), not nums[l .. r]:

  • nums[l] compares s[l] with s[l-1], but s[l-1] is outside the substring — so it must be excluded.

This gives:

bit.query(r + 1) - bit.query(l + 1)

Why excluding nums[l] matters: The minimum deletions to make s[l..r] alternating equals the number of adjacent-equal pairs strictly inside the range. The pair at index l (i.e., s[l-1], s[l]) is irrelevant because s[l-1] is not part of the substring.

Wrong variants to watch for:

bit.query(r) - bit.query(l)        # both off by one
bit.query(r + 1) - bit.query(l)    # mistakenly includes nums[l]

Pitfall 3: Reading the Wrong Query Length

Type-1 queries have length 2 ([1, j]) while type-2 queries have length 3 ([2, l, r]). Unpacking with a fixed pattern fails:

Wrong:

_, l, r = q   # crashes on a [1, j] query (not enough values to unpack)

Correct: Branch on q[0] first, then unpack each type with its own arity:

if q[0] == 1:
    j = q[1]
    ...
else:
    _, l, r = q
    ...

Why the Adjacent-Pair Insight Is the Real Key

The deepest pitfall is algorithmic, not implementational: assuming you must somehow track the alternating structure of the whole substring. The crucial observation is that the minimum deletions to make a string alternating equals the count of adjacent equal pairs. Each such pair forces exactly one deletion, and removing one character of each equal pair is sufficient. Reducing the problem to "count 1s in nums[l+1..r]" is what makes the Fenwick Tree applicable and turns an apparently complex string problem into a standard range-sum-with-point-update task.

Ready to land your dream job?

Unlock your dream job with a 5-minute quiz for a personalized study roadmap!

Get My Roadmap
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Get a Personalized Study Roadmap:

What's the output of running the following function using the following tree as input?

1def serialize(root):
2    res = []
3    def dfs(root):
4        if not root:
5            res.append('x')
6            return
7        res.append(root.val)
8        dfs(root.left)
9        dfs(root.right)
10    dfs(root)
11    return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4    StringJoiner res = new StringJoiner(" ");
5    serializeDFS(root, res);
6    return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10    if (root == null) {
11        result.add("x");
12        return;
13    }
14    result.add(Integer.toString(root.val));
15    serializeDFS(root.left, result);
16    serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2    let res = [];
3    serialize_dfs(root, res);
4    return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8    if (!root) {
9        res.push("x");
10        return;
11    }
12    res.push(root.val);
13    serialize_dfs(root.left, res);
14    serialize_dfs(root.right, res);
15}
16

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More