Maximum Nesting Depth of Two Valid Parentheses Strings in Python

A Valid Parentheses String (VPS) consists only of "(" and ")" characters and satisfies specific structural properties. The goal is to split a VPS into two disjoint subsequences A and B such that the maximum nesting depth is minimized.

Understanding Valid Parentheses Strings

A string is a Valid Parentheses String if ?

  • It is the empty string, or

  • It can be written as AB, where A and B are VPS's, or

  • It can be written as (A), where A is a VPS.

Nesting Depth Definition

The nesting depth of a VPS is defined recursively ?

  • depth("") = 0

  • depth(A + B) = max of depth(A), depth(B), where A and B are VPS's

  • depth("(" + A + ")") = 1 + depth(A), where A is a VPS

Algorithm Approach

The strategy is to balance the parentheses between two groups to minimize the maximum depth. We maintain two counters c1 and c2 representing the current depth of groups A and B ?

  • For opening parentheses: assign to the group with smaller current depth

  • For closing parentheses: assign to the group with larger current depth

Implementation

class Solution:
    def maxDepthAfterSplit(self, seq):
        n = len(seq)
        result = [0] * n
        c1, c2 = 0, 0  # Counters for group A and B depths
        
        for i in range(n):
            if seq[i] == '(':
                if c1 < c2:
                    c1 += 1
                    # result[i] remains 0 (group A)
                else:
                    c2 += 1
                    result[i] = 1  # group B
            else:  # seq[i] == ')'
                if c1 > c2:
                    c1 -= 1
                    # result[i] remains 0 (group A)
                else:
                    c2 -= 1
                    result[i] = 1  # group B
        
        return result

# Test the solution
solution = Solution()
test_string = "()(())()"
result = solution.maxDepthAfterSplit(test_string)
print(f"Input: {test_string}")
print(f"Output: {result}")
Input: ()(())()
Output: [0, 0, 0, 1, 0, 1, 0, 0]

How It Works

Let's trace through the example "()(())()" step by step ?

def trace_algorithm(seq):
    n = len(seq)
    result = [0] * n
    c1, c2 = 0, 0
    
    print(f"Processing: {seq}")
    print("i | char | c1 | c2 | result[i] | action")
    print("-" * 40)
    
    for i in range(n):
        char = seq[i]
        if char == '(':
            if c1 < c2:
                c1 += 1
                action = "assign to A"
            else:
                c2 += 1
                result[i] = 1
                action = "assign to B"
        else:  # char == ')'
            if c1 > c2:
                c1 -= 1
                action = "close A"
            else:
                c2 -= 1
                result[i] = 1
                action = "close B"
        
        print(f"{i} |  {char}   | {c1}  | {c2}  |    {result[i]}     | {action}")
    
    return result

trace_algorithm("()(())()")
Processing: ()(())()
i | char | c1 | c2 | result[i] | action
----------------------------------------
0 |  (   | 1  | 0  |    0     | assign to A
1 |  )   | 0  | 0  |    0     | close A
2 |  (   | 1  | 0  |    0     | assign to A
3 |  (   | 1  | 1  |    1     | assign to B
4 |  )   | 1  | 0  |    1     | close B
5 |  )   | 0  | 0  |    0     | close A
6 |  (   | 1  | 0  |    0     | assign to A
7 |  )   | 0  | 0  |    0     | close A

Key Points

  • The algorithm ensures balanced distribution of parentheses between two groups

  • Time complexity: O(n) where n is the length of the string

  • Space complexity: O(n) for the result array

  • The result array encodes group assignment: 0 for group A, 1 for group B

Conclusion

This algorithm efficiently splits a valid parentheses string into two subsequences while minimizing the maximum nesting depth. The greedy approach of always assigning to the group with smaller depth ensures optimal distribution.

Updated on: 2026-03-25T08:17:39+05:30

403 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements