Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1111. Maximum Nesting Depth of Two Valid Parentheses Strings - Leetcode Solution

Problem Description

The "Maximum Nesting Depth of Two Valid Parentheses Strings" problem asks you to take a valid parentheses string seq (a string consisting only of '(' and ')' that is balanced and properly nested), and split it into two disjoint valid parentheses subsequences A and B. Each parenthesis in seq must be assigned to either A or B, and every parenthesis is assigned to exactly one of them.

The goal is to assign each parenthesis in seq to A or B such that the maximum nesting depth between A and B is minimized. The output should be an array of integers of the same length as seq, where the i-th element is 0 if the i-th parenthesis is assigned to A and 1 if assigned to B.

Constraints:

  • Each parenthesis is assigned to exactly one of A or B.
  • Both A and B will be valid parentheses strings.
  • The solution should minimize the maximum depth among A and B.

Thought Process

When first approaching this problem, you might think about generating all possible assignments and checking which one minimizes the maximum depth. However, this brute-force approach is computationally expensive and not practical for larger inputs.

Instead, we can look for patterns in how nesting depth works. The key insight is that the depth of a parenthesis at position i depends on how many open parentheses have not yet been closed at that point. If we alternate assigning parentheses at increasing depths, we can "split" the deep nestings between A and B to keep their maximum depths as balanced as possible.

This leads to the idea of assigning parentheses based on their current depth: for example, if the current depth is even, assign to A; if odd, assign to B (or vice versa).

Solution Approach

Here’s how we can implement the optimized solution step by step:

  1. Track the current depth: Initialize a variable depth to 0. As you iterate through seq, increment depth when you see '(', and decrement when you see ')'.
  2. Assign based on depth parity: For each character:
    • If it's '(', increment depth after processing, and assign it to depth % 2 (or 1 - (depth % 2) depending on convention).
    • If it's ')', assign it to depth % 2 before decrementing depth (because the closing parenthesis corresponds to the same nesting level as its matching open).
  3. Build the answer array: For each parenthesis, append 0 or 1 to the result array based on the assignment.
  4. Return the result: This array represents the assignment of each parenthesis to A or B such that both subsequences remain valid and the maximum depth is minimized.

This method ensures that at every nesting level, the parentheses are distributed as evenly as possible between A and B.

Example Walkthrough

Let's take seq = "(()())" as an example.

  1. Start with depth = 0 and an empty result array.
  2. Iterate through each character:
    • i=0, char='(' : increment depth to 1, assign to 1 (since depth is now 1), result = [1]
    • i=1, char='(' : increment depth to 2, assign to 0 (depth is 2), result = [1,0]
    • i=2, char=')' : assign to 0 (depth is 2), then decrement depth to 1, result = [1,0,0]
    • i=3, char='(' : increment depth to 2, assign to 0, result = [1,0,0,0]
    • i=4, char=')' : assign to 0 (depth is 2), then decrement depth to 1, result = [1,0,0,0,0]
    • i=5, char=')' : assign to 1 (depth is 1), then decrement depth to 0, result = [1,0,0,0,0,1]
  3. The final assignment is [1,0,0,0,0,1]. This means that the first and last parentheses are assigned to B, and the rest to A, resulting in both subsequences having a maximum depth of 1.

Code Implementation

class Solution:
    def maxDepthAfterSplit(self, seq: str) -> list[int]:
        res = []
        depth = 0
        for c in seq:
            if c == '(':
                depth += 1
                res.append(depth % 2)
            else:
                res.append(depth % 2)
                depth -= 1
        return res
      
class Solution {
public:
    vector<int> maxDepthAfterSplit(string seq) {
        vector<int> res;
        int depth = 0;
        for (char c : seq) {
            if (c == '(') {
                ++depth;
                res.push_back(depth % 2);
            } else {
                res.push_back(depth % 2);
                --depth;
            }
        }
        return res;
    }
};
      
class Solution {
    public int[] maxDepthAfterSplit(String seq) {
        int n = seq.length();
        int[] res = new int[n];
        int depth = 0;
        for (int i = 0; i < n; ++i) {
            if (seq.charAt(i) == '(') {
                depth++;
                res[i] = depth % 2;
            } else {
                res[i] = depth % 2;
                depth--;
            }
        }
        return res;
    }
}
      
var maxDepthAfterSplit = function(seq) {
    let res = [];
    let depth = 0;
    for (let c of seq) {
        if (c === '(') {
            depth++;
            res.push(depth % 2);
        } else {
            res.push(depth % 2);
            depth--;
        }
    }
    return res;
};
      

Time and Space Complexity

Brute-force approach:

  • Would require generating all possible assignments (exponential in length of seq), and checking validity and depth for each, leading to O(2^n) time complexity.
Optimized approach:
  • We iterate over seq exactly once, doing O(1) work for each character, so the time complexity is O(n), where n is the length of seq.
  • We use an output array of size n, so the space complexity is also O(n).

This is optimal for this problem, as we must examine every character at least once.

Summary

The key insight for this problem is to distribute parentheses between two groups in such a way that their maximum nesting depths are balanced. By assigning parentheses based on the parity (even/odd) of their depth, we ensure that both groups remain valid and their depths are as close as possible. This leads to an efficient O(n) solution that is elegant, simple, and easy to implement.