Pairs involved in Balanced Parentheses (original) (raw)

Last Updated : 11 Jul, 2025

Given a string of brackets, the task is to find the number of pairs of brackets involved in a balanced sequence in a given range.

Examples :

Input : ((())(() Range : 1 5 Range : 3 8 Output : 2 2 Explanation : In range 1 to 5 ((()), there are the two pairs. In range 3 to 8 ()) ((), there are the two pairs.

Input : )()())) Range : 1 2 Range : 4 7 Output : 0 1 Explanation : In range 1 to 2 )( there is no pair. In range 4 to 7 ())), there is the only pair

Prerequisite : Segment Trees

Approach :

Here, in segment tree, for each node, keep some simple elements, like integers or sets or vectors or etc.
For each node keep three integers :

Now, having these variables, queries can be answered easily using segment tree.

Below is the implementation of the above approach :

C++ `

// CPP code to find the number of pairs // involved in balanced parentheses #include <bits/stdc++.h> using namespace std;

// Our struct node struct node {

// three variables required
int t, o, c;

};

// Declare array of nodes of very big // size which acts as segment tree here. struct node tree_arr[5 * 1000];

// To build a segment tree we pass 1 as // 'id' 0 as 'l' and l as 'n'. // Here, we consider query's interval as [x, y) void build(int id, int l, int r, string s) { /* this base condition is common to build any segment tree*/ // This is the base // Only one element left if (r - l < 2) {

    // If that element is open bracket
    if (s[l] == '(')
        tree_arr[id].o = 1;
    
    // If that element is open bracket
    else
        tree_arr[id].c = 1;
    return;
}

// Next three lines are common 
// for any segment tree.
int mid = (l + r) / 2;

// for left tree
build(2 * id, l, mid, s); 

// for right tree
build(2 * id + 1, mid, r, s);

// Here we take minimum of left tree
// opening brackets and right tree
// closing brackets
int tmp = min(tree_arr[2 * id].o, 
              tree_arr[2 * id + 1].c);

// we add that to our answer.
tree_arr[id].t = tree_arr[2 * id].t + 
          tree_arr[2 * id + 1].t + tmp;

// Remove the answer from opening brackets
tree_arr[id].o = tree_arr[2 * id].o + 
           tree_arr[2 * id + 1].o - tmp;

// Remove the answer from opening brackets
tree_arr[id].c = tree_arr[2 * id].c + 
           tree_arr[2 * id + 1].c - tmp;

}

// This will return the answer for each query. // Here we consider query's interval as [x, y) node segment(int x, int y, int id, int l, int r, string s) { // If the given interval is out of range if (l >= y || x >= r) { struct node tem; tem.t = 0; tem.o = 0; tem.c = 0; return tem; }

// If the given interval completely lies
if (x <= l && r <= y)
    return tree_arr[id];

// Next three lines are common for 
// any segment tree.
int mid = (l + r) / 2;

// For left tree
struct node a = 
       segment(x, y, 2 * id, l, mid, s);

// For right tree
struct node b = 
       segment(x, y, 2 * id + 1, mid, r, s);

// Same as made in build function
int temp;
temp = min(a.o, b.c);
struct node vis;
vis.t = a.t + b.t + temp;
vis.o = a.o + b.o - temp;
vis.c = a.c + b.c - temp;

return vis;

}

// Driver code int main() { string s = "((())(()"; int n = s.size();

// range for query
int a = 3, b = 8;

build(1, 0, n, s);

// Here we consider query's interval as [a, b)
// We subtract 1 from 'a' because indexes start
// from 0.
struct node p = segment(a-1, b, 1, 0, n, s);
cout << p.t << endl;

return 0;

}

Java

// Java code for the above approach import java.util.Scanner;

// Our struct node class Node { int t, o, c; }

class SegmentTree { Node[] tree_arr;

// Declare array of nodes of very big size which acts as segment tree here. SegmentTree(int n) { tree_arr = new Node[5 * n]; for (int i = 0; i < 5 * n; i++) { tree_arr[i] = new Node(); } }

// To build a segment tree we pass 1 as 'id' 0 as 'l' and l as 'n'. // Here, we consider query's interval as [x, y) void build(int id, int l, int r, String s) { /* this base condition is common to build any segment tree*/ // This is the base // Only one element left if (r - l < 2) { // If that element is open bracket if (s.charAt(l) == '(') { tree_arr[id].o = 1; } else { tree_arr[id].c = 1; } return; }

// Next three lines are common 
// for any segment tree.
int mid = (l + r) / 2;

// for left tree
build(2 * id, l, mid, s);

// for right tree
build(2 * id + 1, mid, r, s);

// Here we take minimum of left tree opening brackets and right tree closing brackets
int temp = Math.min(tree_arr[2 * id].o, tree_arr[2 * id + 1].c);

// we add that to our answer.
tree_arr[id].t = tree_arr[2 * id].t + tree_arr[2 * id + 1].t + temp;

// Remove the answer from opening brackets
tree_arr[id].o = tree_arr[2 * id].o + tree_arr[2 * id + 1].o - temp;

// Remove the answer from opening brackets
tree_arr[id].c = tree_arr[2 * id].c + tree_arr[2 * id + 1].c - temp;

}

// This will return the answer for each query. // Here we consider query's interval as [x, y) Node segment(int x, int y, int id, int l, int r, String s) { // If the given interval is out of range if (l >= y || x >= r) { Node temp = new Node(); temp.t = 0; temp.o = 0; temp.c = 0; return temp; }

// If the given interval completely lies
if (x <= l && r <= y) {
  return tree_arr[id];
}

// Next three lines are common for 
// any segment tree.
int mid = (l + r) / 2;

// For left tree
Node a = segment(x, y, 2 * id, l, mid, s);
// For right tree
Node b = segment(x, y, 2 * id + 1, mid, r, s);
int temp = Math.min(a.o, b.c);
Node res = new Node();
res.t = a.t + b.t + temp;
res.o = a.o + b.o - temp;
res.c = a.c + b.c - temp;
return res;

}

// Driver code public static void main(String[] args) {

String s = "((())(()";
int n = s.length();
SegmentTree obj = new SegmentTree(n);
obj.build(1, 0, n, s);
int a = 3, b = 8;
Node p = obj.segment(a - 1, b, 1, 0, n, s);
System.out.println(p.t);

} }

// This code is contributed by Potta Lokesh

Python3

Python3 code to find the number of pairs

involved in balanced parentheses

Our struct node

class node : def init(self): # three variables required self.t = 0 self.o = 0 self.c = 0

Declare array of nodes of very big

size which acts as segment tree here.

tree_arr = [node()]*(5 * 1000)

To build asegmenttree we pass 1 as

'id' 0 as 'l' and l as 'n'.

Here, we consider query's interval as [x, y)

def build(id, l, r, s): global tree_arr

tree_arr[id] = node()

# this base condition is common to 
# build any segment tree
# This is the base
# Only one element left
if (r - l < 2) :
    
    # If that element is open bracket
    if (s[l] == "("):
        tree_arr[id].o = 1
    
    # If that element is open bracket
    else:
        tree_arr[id].c = 1
    return

# Next three lines are common 
# for any segment tree.
mid = int( (l + r) / 2)

# for left tree
build(2 * id, l, mid, s) 

# for right tree
build(2 * id + 1, mid, r, s)

# Here we take minimum of left tree
# opening brackets and right tree
# closing brackets
tmp = min(tree_arr[2 * id].o, 
            tree_arr[2 * id + 1].c)
# we add that to our answer.
tree_arr[id].t = tree_arr[2 * id].t + \
                tree_arr[2 * id + 1].t + tmp

# Remove the answer from opening brackets
tree_arr[id].o = tree_arr[2 * id].o + \
                tree_arr[2 * id + 1].o - tmp

# Remove the answer from opening brackets
tree_arr[id].c = tree_arr[2 * id].c + \
                tree_arr[2 * id + 1].c - tmp

This will return the answer for each query.

Here we consider query's interval as [x, y)

def segment(x, y, id, l, r, s):

global tree_arr

# If the given interval is out of range
if (l >= y or x >= r) :
    tem= node()
    tem.t = 0
    tem.o = 0
    tem.c = 0
    return tem

# If the given interval completely lies
if (x <= l and r <= y):
    return tree_arr[id]

# Next three lines are common for 
# any segment tree.
mid = int((l + r) / 2)

# For left tree
a = segment(x, y, 2 * id, l, mid, s)

# For right tree
b = segment(x, y, 2 * id + 1, mid, r, s)

# Same as made in build function
temp= 0
temp = min(a.o, b.c)
vis = node()

vis.t = a.t + b.t + temp
vis.o = a.o + b.o - temp
vis.c = a.c + b.c - temp
return vis

Driver code

s = "((())(()" n = len(s)

range for query

a = 3 b = 8

build(1, 0, n, s)

Here we consider query's interval as [a, b)

We subtract 1 from 'a' because indexes start

from 0.

p = segment(a-1, b, 1, 0, n, s) print(p.t )

This code is contributed by Arnab Kundu

C#

using System;

class SegmentTree { public Node[] tree_arr;

// Our struct node
public struct Node { public int t, o, c; }

// Declare array of nodes of very big size which acts as
// segment tree here.
public SegmentTree(int n)
{
    tree_arr = new Node[5 * n];
    for (int i = 0; i < 5 * n; i++) {
        tree_arr[i] = new Node();
    }
}

// To build a segment tree we pass 1 as 'id' 0 as 'l'
// and l as 'n'. Here, we consider query's interval as
// [x, y)
public void build(int id, int l, int r, string s)
{
    /* this base condition is common to
           build any segment tree*/
    // This is the base
    // Only one element left
    if (r - l < 2) {
        // If that element is open bracket
        if (s[l] == '(') {
            tree_arr[id].o = 1;
        }
        else {
            tree_arr[id].c = 1;
        }
        return;
    }

    // Next three lines are common
    // for any segment tree.
    int mid = (l + r) / 2;

    // for left tree
    build(2 * id, l, mid, s);

    // for right tree
    build(2 * id + 1, mid, r, s);

    // Here we take minimum of left tree opening
    // brackets and right tree closing brackets
    int temp = Math.Min(tree_arr[2 * id].o,
                        tree_arr[2 * id + 1].c);

    // we add that to our answer.
    tree_arr[id].t = tree_arr[2 * id].t
                     + tree_arr[2 * id + 1].t + temp;

    // Remove the answer from opening brackets
    tree_arr[id].o = tree_arr[2 * id].o
                     + tree_arr[2 * id + 1].o - temp;

    // Remove the answer from opening brackets
    tree_arr[id].c = tree_arr[2 * id].c
                     + tree_arr[2 * id + 1].c - temp;
}

// This will return the answer for each query.
// Here we consider query's interval as [x, y)
public Node segment(int x, int y, int id, int l, int r,
                    string s)
{
    // If the given interval is out of range
    if (l >= y || x >= r) {
        Node temp = new Node();
        temp.t = 0;
        temp.o = 0;
        temp.c = 0;
        return temp;
    }

    // If the given interval completely lies
    if (x <= l && r <= y) {
        return tree_arr[id];
    }

    // Next three lines are common for
    // any segment tree.
    int mid = (l + r) / 2;

    // For left tree
    Node a = segment(x, y, 2 * id, l, mid, s);
    // For right tree
    Node b = segment(x, y, 2 * id + 1, mid, r, s);
    int te = Math.Min(a.o, b.c);
    Node res = new Node();
    res.t = a.t + b.t + te;
    res.o = a.o + b.o - te;
    res.c = a.c + b.c - te;
    return res;
}

// Driver code
public static void Main(string[] args)
{

    string s = "((())(()";
    int n = s.Length;
    SegmentTree obj = new SegmentTree(n);
    obj.build(1, 0, n, s);
    int a = 3, b = 8;
    Node p = obj.segment(a - 1, b, 1, 0, n, s);
    Console.WriteLine(p.t);
}

}

JavaScript

// Our struct node function node() {

// three variables required
this.t = 0; 
this.o = 0;
this.c = 0;

}

// Declare array of nodes of very big // size which acts as segment tree here. let tree_arr = new Array(5 * 1000);

// To build a segment tree we pass 1 as // 'id' 0 as 'l' and l as 'n'. // Here, we consider query's interval as [x, y) function build(id, l, r, s) { /* this base condition is common to build any segment tree*/ // This is the base // Only one element left if (r - l < 2) {

    // If that element is open bracket
    if (s[l] == '(')
        tree_arr[id].o = 1;
    
    // If that element is open bracket
    else
        tree_arr[id].c = 1;
    return;
}

// Next three lines are common 
// for any segment tree.
let mid = Math.floor((l + r) / 2);

// for left tree
build(2 * id, l, mid, s); 

// for right tree
build(2 * id + 1, mid, r, s);

// Here we take minimum of left tree
// opening brackets and right tree
// closing brackets
let tmp = Math.min(tree_arr[2 * id].o, 
              tree_arr[2 * id + 1].c);

// we add that to our answer.
tree_arr[id].t = tree_arr[2 * id].t + 
          tree_arr[2 * id + 1].t + tmp;

// Remove the answer from opening brackets
tree_arr[id].o = tree_arr[2 * id].o + 
           tree_arr[2 * id + 1].o - tmp;

// Remove the answer from opening brackets
tree_arr[id].c = tree_arr[2 * id].c + 
           tree_arr[2 * id + 1].c - tmp;

}

// This will return the answer for each query. // Here we consider query's interval as [x, y) function segment(x, y, id, l, r, s) { // If the given interval is out of range if (l >= y || x >= r) { let tem = new node(); return tem; }

// If the given interval completely lies
if (x <= l && r <= y)
    return tree_arr[id];

// Next three lines are common for 
// any segment tree.
let mid = Math.floor((l + r) / 2);

// For left tree
let a = 
       segment(x, y, 2 * id, l, mid, s);

// For right tree
let b = 
       segment(x, y, 2 * id + 1, mid, r, s);

// Same as made in build function
let temp;
temp = Math.min(a.o, b.c);
let vis = new node();
vis.t = a.t + b.t + temp;
vis.o = a.o + b.o - temp;
vis.c = a.c + b.c - temp;

return vis;

}

// Driver code function main() { let s = "((())(()"; let n = s.length;

// range for query
let a = 3, b = 8;

build(1, 0, n, s);

// Here we consider query's interval as [a, b)
// We subtract 1 from 'a' because indexes start
// from 0.
let p = segment(a-1, b, 1, 0, n, s);
console.log(p.t);

return 0;

}

main();

`

Time Complexity: O(N), where N is the length of the string.
Auxiliary Space: O(N), for storing the segment tree.