Open In App

Range Queries for Longest Correct Bracket Subsequence

Last Updated : 27 Apr, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Given a bracket sequence or in other words a string S of length n, consisting of characters ‘(‘ and ‘)’. Find the length of the maximum correct bracket subsequence of sequence for a given query range

Note: A correct bracket sequence is the one that has matched bracket pairs or which contains another nested correct bracket sequence. For e.g (), (()), ()() are some correct bracket sequence

Examples:

Input: S = ())(())(())(
Output: 10
Explanation:  Longest Correct Bracket Subsequence is ()(())(())

Input: S = ())(())(())(0
Output: 0

Range Queries for Longest Correct Bracket Subsequence using segment trees:

To solve the problem follow the below idea:

Segment Trees can be used to solve this problem efficiently At each node of the segment tree, we store the following:

  • Number of correctly matched pairs of brackets
  • Number of unused open brackets
  • Number of unused closed brackets

(unused open bracket – means they can’t be matched with any closing bracket, for e.g S = )( contains an unused open and an unused closed bracket) For each interval [L, R], we can match X number of unused open brackets ‘(‘ in interval [L, MID] with unused closed brackets ‘)’ in interval [MID + 1, R] where X = minimum(number of unused ‘(‘ in [L, MID], number of unused ‘)’ in [MID + 1, R]) Hence, X is also the number of correctly matched pairs built by combination. So, for interval [L, R] 

  • Total number of correctly matched pairs becomes the sum of correctly matched pairs in left child and correctly matched pairs in right child and number of combinations of unused ‘(‘ and unused ‘)’ from left and right child respectively

a[L, R] = a[L, MID] + a[MID + 1, R] + X

  • Total number of unused open brackets becomes the sum of unused open brackets in left child and unused open brackets in right child minus X (minus – because we used X unused ‘(‘ from left child to match with unused ‘) from right child)

a[L, R] = b[L, MID] + b[MID + 1, R] – X

  • Similarly, for unused closed brackets, following relation holds

a[L, R] = c[L, MID] + c[MID + 1, R] – X

where a, b and c are the representations described above for each node to be stored in

Below is the implementation of the above approach: 

C++




/* CPP Program to find the longest correct
bracket subsequence in a given range */
#include <bits/stdc++.h>
using namespace std;
 
/* Declaring Structure for storing
three values in each segment tree node */
struct Node {
    int pairs;
    int open; // unused
    int closed; // unused
 
    Node() { pairs = open = closed = 0; }
};
 
// A utility function to get the middle index from corner
// indexes.
int getMid(int s, int e) { return s + (e - s) / 2; }
 
// Returns Parent Node after merging its left and right
// child
Node merge(Node leftChild, Node rightChild)
{
    Node parentNode;
    int minMatched = min(leftChild.open, rightChild.closed);
    parentNode.pairs
        = leftChild.pairs + rightChild.pairs + minMatched;
    parentNode.open
        = leftChild.open + rightChild.open - minMatched;
    parentNode.closed
        = leftChild.closed + rightChild.closed - minMatched;
    return parentNode;
}
 
// A recursive function that constructs Segment Tree
// for string[ss..se]. si is index of current node in
// segment tree st
void constructSTUtil(char str[], int ss, int se, Node* st,
                     int si)
{
    // If there is one element in string, store it in
    // current node of segment tree and return
    if (ss == se) {
 
        // since it contains one element, pairs
        // will be zero
        st[si].pairs = 0;
 
        // check whether that one element is opening
        // bracket or not
        st[si].open = (str[ss] == '(' ? 1 : 0);
 
        // check whether that one element is closing
        // bracket or not
        st[si].closed = (str[ss] == ')' ? 1 : 0);
 
        return;
    }
 
    // If there are more than one elements, then recur
    // for left and right subtrees and store the relation
    // of values in this node
    int mid = getMid(ss, se);
    constructSTUtil(str, ss, mid, st, si * 2 + 1);
    constructSTUtil(str, mid + 1, se, st, si * 2 + 2);
 
    // Merge left and right child into the Parent Node
    st[si] = merge(st[si * 2 + 1], st[si * 2 + 2]);
}
 
/* Function to construct segment tree from given
string. This function allocates memory for segment
tree and calls constructSTUtil() to fill the
allocated memory */
Node* constructST(char str[], int n)
{
    // Allocate memory for segment tree
 
    // Height of segment tree
    int x = (int)(ceil(log2(n)));
 
    // Maximum size of segment tree
    int max_size = 2 * (int)pow(2, x) - 1;
 
    // Declaring array of structure Allocate memory
    Node* st = new Node[max_size];
 
    // Fill the allocated memory st
    constructSTUtil(str, 0, n - 1, st, 0);
 
    // Return the constructed segment tree
    return st;
}
 
/* A Recursive function to get the desired
Maximum Sum Sub-Array,
The following are parameters of the function-
 
st     --> Pointer to segment tree
si --> Index of the segment tree Node
ss & se --> Starting and ending indexes of the
            segment represented by
                current Node, i.e., tree[index]
qs & qe --> Starting and ending indexes of query range */
Node queryUtil(Node* st, int ss, int se, int qs, int qe,
               int si)
{
    // No overlap
    if (ss > qe || se < qs) {
 
        // returns a Node for out of bounds condition
        Node nullNode;
        return nullNode;
    }
 
    // Complete overlap
    if (ss >= qs && se <= qe) {
        return st[si];
    }
 
    // Partial Overlap Merge results of Left
    // and Right subtrees
    int mid = getMid(ss, se);
    Node left = queryUtil(st, ss, mid, qs, qe, si * 2 + 1);
    Node right
        = queryUtil(st, mid + 1, se, qs, qe, si * 2 + 2);
 
    // merge left and right subtree query results
    Node res = merge(left, right);
    return res;
}
 
/* Returns the maximum length correct bracket
subsequencebetween start and end
It mainly uses queryUtil(). */
int query(Node* st, int qs, int qe, int n)
{
    Node res = queryUtil(st, 0, n - 1, qs, qe, 0);
 
    // since we are storing numbers pairs
    // and have to return maximum length, hence
    // multiply no of pairs by 2
    return 2 * res.pairs;
}
 
// Driver Code
int main()
{
    char str[] = "())(())(())(";
    int n = strlen(str);
 
    // Build segment tree from given string
    Node* st = constructST(str, n);
 
      // Function call
    int startIndex = 0, endIndex = 11;
    cout << "Maximum Length Correct Bracket"
            " Subsequence between "
         << startIndex << " and " << endIndex << " = "
         << query(st, startIndex, endIndex, n) << endl;
 
    startIndex = 1, endIndex = 2;
    cout << "Maximum Length Correct Bracket"
            " Subsequence between "
         << startIndex << " and " << endIndex << " = "
         << query(st, startIndex, endIndex, n) << endl;
 
    return 0;
}


Java




// Java Program to find the longest correct bracket
// subsequence in a given range
import java.util.*;
import java.lang.*;
import java.io.*;
 
// Declaring Structure for storing three values in each segment tree node
class Node {
    int pairs;
    int open; // unused
    int closed; // unused
  
    Node() {
        pairs = 0;
        open = 0;
        closed = 0;
    }
}
 
class SegmentTree {
    Node[] st;
    int n;
  
    // A utility function to get the middle index from corner indexes.
    int getMid(int s, int e) {
        return s + (e - s) / 2;
    }
  
    // Returns Parent Node after merging its left and right child
    Node merge(Node leftChild, Node rightChild) {
        Node parentNode = new Node();
        int minMatched = Math.min(leftChild.open, rightChild.closed);
        parentNode.pairs = leftChild.pairs + rightChild.pairs + minMatched;
        parentNode.open = leftChild.open + rightChild.open - minMatched;
        parentNode.closed = leftChild.closed + rightChild.closed - minMatched;
        return parentNode;
    }
  
    // A recursive function that constructs Segment Tree for string[ss..se].
    // si is index of current node in segment tree st
    void constructSTUtil(String str, int ss, int se, int si) {
        // If there is one element in string, store it in current node of
        // segment tree and return
        if (ss == se) {
            // since it contains one element, pairs will be zero
            st[si].pairs = 0;
            // check whether that one element is opening bracket or not
            st[si].open = (str.charAt(ss) == '(') ? 1 : 0;
            // check whether that one element is closing bracket or not
            st[si].closed = (str.charAt(ss) == ')') ? 1 : 0;
            return;
        }
  
        // If there are more than one elements, then recur for left and right
        // subtrees and store the relation of values in this node
        int mid = getMid(ss, se);
        constructSTUtil(str, ss, mid, si * 2 + 1);
        constructSTUtil(str, mid + 1, se, si * 2 + 2);
  
        // Merge left and right child into the Parent Node
        st[si] = merge(st[si * 2 + 1], st[si * 2 + 2]);
    }
  
    // Function to construct segment tree from given string.
    // This function allocates memory for segment tree and calls
    // constructSTUtil() to fill the allocated memory
    void constructST(String str) {
        // Allocate memory for segment tree
        // Height of segment tree
        int x = (int) (Math.ceil(Math.log(n) / Math.log(2)));
  
        // Maximum size of segment tree
        int max_size = 2 * (int) Math.pow(2, x) - 1;
  
        st = new Node[max_size];
        for (int i = 0; i < max_size; i++)
            st[i] = new Node();
  
        // Fill the allocated memory st
        constructSTUtil(str, 0, n - 1, 0);
    }
  
    // A Recursive function to get the desired Maximum Sum Sub-Array,
    // The following are parameters of the function-
    // st     --> Pointer to segment tree
    // si --> Index of the segment tree Node
    // ss & se --> Starting and ending indexes of the segment
    // represented by current Node, i.e., tree[index]
    // qs & qe --> Starting and ending indexes of query range
    Node queryUtil(int ss, int se, int qs, int qe, int si) {
        // No overlap
        if (ss > qe || se < qs)
            // returns a Node for out of bounds condition
            return new Node();
  
        // Complete overlap
        if (ss >= qs && se <= qe)
            return st[si];
  
        // Partial Overlap Merge results of Left and Right subtrees
        int mid = getMid(ss, se);
        Node left = queryUtil(ss, mid, qs, qe, si * 2 + 1);
        Node right = queryUtil(mid + 1, se, qs, qe, si * 2 + 2);
        return merge(left, right);
    }
  
    // The function to get the maximum length of correct bracket subsequence
    // for given range. The following are parameters of the function-
    // st --> Pointer to segment tree
    // qs & qe --> Starting and ending indexes of query range
    int query(int qs, int qe) {
        Node node = queryUtil(0, n - 1, qs, qe, 0);
        return 2 * node.pairs;
    }
  
    // Driver code
    public static void main(String args[]) {
        String str = "())(())(())(";
        int n = str.length();
        SegmentTree tree = new SegmentTree();
        tree.n = n;
        tree.constructST(str);
        int qs = 0;
        int qe = n - 1;
        System.out.println("Maximum Length Correct Bracket Subsequence between " + qs + " and " + qe + " = " + tree.query(qs, qe));
  
        qs = 1;
        qe = 2;
        System.out.println("Maximum Length Correct Bracket Subsequence between " + qs + " and " + qe + " = " + tree.query(qs, qe));
    }
}


Python3




# Python Program to find the longest correct bracket
# subsequence in a given range
import math
# Declaring Structure for storing three values in each segment tree node
class Node:
    def __init__(self):
        self.pairs = 0
        self.open = 0 # unused
        self.closed = 0 # unused
 
# A utility function to get the middle index from corner indexes.
def getMid(s: int, e: int) -> int:
    return s + (e - s) // 2
 
# Returns Parent Node after merging its left and right child
def merge(leftChild: Node, rightChild: Node) -> Node:
    parentNode = Node()
    minMatched = min(leftChild.open, rightChild.closed)
    parentNode.pairs = leftChild.pairs + rightChild.pairs + minMatched
    parentNode.open = leftChild.open + rightChild.open - minMatched
    parentNode.closed = leftChild.closed + rightChild.closed - minMatched
    return parentNode
 
# A recursive function that constructs Segment Tree for string[ss..se].
# si is index of current node in segment tree st
def constructSTUtil(str: str, ss: int, se: int, st: list, si: int):
    # If there is one element in string, store it in current node of segment tree and return
    if ss == se:
        # since it contains one element, pairs will be zero
        st[si].pairs = 0
        # check whether that one element is opening bracket or not
        st[si].open = 1 if str[ss] == '(' else 0
        # check whether that one element is closing bracket or not
        st[si].closed = 1 if str[ss] == ')' else 0
        return
    # If there are more than one elements, then recur for left and right subtrees
    # and store the relation of values in this node
    mid = getMid(ss, se)
    constructSTUtil(str, ss, mid, st, si * 2 + 1)
    constructSTUtil(str, mid + 1, se, st, si * 2 + 2)
    # Merge left and right child into the Parent Node
    st[si] = merge(st[si * 2 + 1], st[si * 2 + 2])
 
# Function to construct segment tree from given string.
# This function allocates memory for segment tree and calls constructSTUtil()
# to fill the allocated memory
def constructST(str: str, n: int) -> list:
    # Allocate memory for segment tree
    # Height of segment tree
    x = int(math.ceil(math.log2(n)))
    # Maximum size of segment tree
    max_size = 2 * int(2 ** x) - 1
    # Declaring array of structure Allocate memory
    st = [Node() for _ in range(max_size)]
    # Fill the allocated memory st
    constructSTUtil(str, 0, n - 1, st, 0)
    # Return the constructed segment tree
    return st
 
# A Recursive function to get the desired Maximum Sum Sub-Array,
# The following are parameters of the function-
# st     --> Pointer to segment tree
# si --> Index of the segment tree Node
# ss & se --> Starting and ending indexes of the segment
# represented by current Node, i.e., tree[index]
# qs & qe --> Starting and ending indexes of query range
def queryUtil(st: list, ss: int, se: int, qs: int, qe: int, si: int) -> Node:
    # No overlap
    if ss > qe or se < qs:
        # returns a Node for out of bounds condition
        return Node()
    # Complete overlap
    if ss >= qs and se <= qe:
        return st[si]
    # Partial Overlap Merge results of Left and Right subtrees
    mid = getMid(ss, se)
    left = queryUtil(st, ss, mid, qs, qe, si * 2 + 1)
    right = queryUtil(st, mid + 1, se, qs, qe, si * 2 + 2)
    return merge(left, right)
 
# The function to get the maximum length of correct bracket subsequence
# for given range. The following are parameters of the function-
# st --> Pointer to segment tree
# qs & qe --> Starting and ending indexes of query range
def query(st: list, n: int, qs: int, qe: int) -> int:
    node = queryUtil(st, 0, n - 1, qs, qe, 0)
    return 2 * node.pairs
 
# Driver code
def main():
    str = "())(())(())("
    n = len(str)
    st = constructST(str, n)
    qs = 0
    qe = n - 1
    print("Maximum Length Correct Bracket Subsequence between", qs, "and", qe, "=", query(st, n, qs, qe))
 
    qs = 1
    qe = 2
    print("Maximum Length Correct Bracket Subsequence between", qs, "and", qe, "=", query(st, n, qs, qe))
 
if __name__ == '__main__':
    main()
# This code is contributed by Vikram_Shirsat


Javascript




// Javascript Program to find the longest correct bracket
// subsequence in a given range
class Node {
  constructor() {
    this.pairs = 0;
    this.open = 0; // unused
    this.closed = 0; // unused
  }
}
 
// A utility function to get the middle index from corner indexes.
function getMid(s, e) {
  return s + Math.floor((e - s) / 2);
}
 
// Returns Parent Node after merging its left and right child
function merge(leftChild, rightChild) {
  const parentNode = new Node();
  const minMatched = Math.min(leftChild.open, rightChild.closed);
  parentNode.pairs = leftChild.pairs + rightChild.pairs + minMatched;
  parentNode.open = leftChild.open + rightChild.open - minMatched;
  parentNode.closed = leftChild.closed + rightChild.closed - minMatched;
  return parentNode;
}
 
 
// A recursive function that constructs Segment Tree for string[ss..se].
// si is index of current node in segment tree st
function constructSTUtil(str, ss, se, st, si) {
    // If there is one element in string, store it in current node of segment tree and return
  if (ss === se) {
    // since it contains one element, pairs will be zero
    st[si].pairs = 0;
    // check whether that one element is opening bracket or not
    st[si].open = str[ss] === '(' ? 1 : 0;
    // check whether that one element is closing bracket or not
    st[si].closed = str[ss] === ')' ? 1 : 0;
    return;
  }
    // If there are more than one elements, then recur for left and right subtrees
    // and store the relation of values in this node
  const mid = getMid(ss, se);
  constructSTUtil(str, ss, mid, st, si * 2 + 1);
  constructSTUtil(str, mid + 1, se, st, si * 2 + 2);
  // Merge left and right child into the Parent Node
  st[si] = merge(st[si * 2 + 1], st[si * 2 + 2]);
}
 
// Function to construct segment tree from given string.
// This function allocates memory for segment tree and calls constructSTUtil()
// to fill the allocated memory
function constructST(str, n) {
   // Allocate memory for segment tree
  // Height of segment tree
  const x = Math.ceil(Math.log2(n));
  // Maximum size of segment tree
  const max_size = 2 * Math.pow(2, x) - 1;
  // Declaring array of structure Allocate memory
  const st = Array(max_size).fill().map(_ => new Node());
  // Fill the allocated memory st
  constructSTUtil(str, 0, n - 1, st, 0);
  // Return the constructed segment tree
  return st;
}
 
 
// A Recursive function to get the desired Maximum Sum Sub-Array,
// The following are parameters of the function-
// st     --> Pointer to segment tree
// si --> Index of the segment tree Node
// ss & se --> Starting and ending indexes of the segment
// represented by current Node, i.e., tree[index]
// qs & qe --> Starting and ending indexes of query range
function queryUtil(st, ss, se, qs, qe, si) {
  if (ss > qe || se < qs) {
    return new Node();
  }
  if (ss >= qs && se <= qe) {
    return st[si];
  }
  const mid = getMid(ss, se);
  const left = queryUtil(st, ss, mid, qs, qe, si * 2 + 1);
  const right = queryUtil(st, mid + 1, se, qs, qe, si * 2 + 2);
  return merge(left, right);
}
 
 
// The function to get the maximum length of correct bracket subsequence
// for given range. The following are parameters of the function-
// st --> Pointer to segment tree
// qs & qe --> Starting and ending indexes of query range
function query(st, n, qs, qe) {
  const node = queryUtil(st, 0, n - 1, qs, qe, 0);
  return 2 * node.pairs;
}
 
// Driver code
function main() {
  const str = "())(())(())(";
  const n = str.length;
  const st = constructST(str, n);
  let qs = 0;
  let qe = n - 1;
  console.log(`Maximum Length Correct Bracket Subsequence between ${qs} and ${qe} = ${query(st, n, qs, qe)}`);
 
  qs = 1;
  qe = 2;
  console.log(`Maximum Length Correct Bracket Subsequence between ${qs} and ${qe} = ${query(st, n, qs, qe)}`);
}
 
main();
 
// This code is contributed by sdeadityasharma


C#




// C# Program to find the longest correct bracket
// subsequence in a given range
 
using System;
// Declaring Structure for storing three values in each segment tree node    // A utility function to get the middle index from corner indexes.
class Node {
    public int pairs;
    public int open;
    public int closed;
 
    public Node() {
        pairs = 0;
        open = 0;
        closed = 0;
    }
}
 
class SegmentTree {
    public Node[] st;
    public int n;
    // A utility function to get the middle index from corner indexes.
    int getMid(int s, int e) {
        return s + (e - s) / 2;
    }
 // Returns Parent Node after merging its left and right child
    Node merge(Node leftChild, Node rightChild) {
        Node parentNode = new Node();
        int minMatched = Math.Min(leftChild.open, rightChild.closed);
        parentNode.pairs = leftChild.pairs + rightChild.pairs + minMatched;
        parentNode.open = leftChild.open + rightChild.open - minMatched;
        parentNode.closed = leftChild.closed + rightChild.closed - minMatched;
        return parentNode;
    }
    // A recursive function that constructs Segment Tree for string[ss..se].
    // si is index of current node in segment tree st
    void constructSTUtil(string str, int ss, int se, int si) {
         // If there is one element in string, store it in current node of
        // segment tree and return
         
        if (ss == se) {
                   // since it contains one element, pairs will be zero
            st[si].pairs = 0;
            // check whether that one element is opening bracket or not
            st[si].open = (str[ss] == '(') ? 1 : 0;
              // check whether that one element is closing bracket or not
        // If there are more than one elements, then recur for left and right
        // subtrees and store the relation of values in this node
            st[si].closed = (str[ss] == ')') ? 1 : 0;
            return;
        }
 
        // If there are more than one elements, then recur for left and right
        // subtrees and store the relation of values in this node
        int mid = getMid(ss, se);
        constructSTUtil(str, ss, mid, si * 2 + 1);
        constructSTUtil(str, mid + 1, se, si * 2 + 2);
 // Merge left and right child into the Parent Node
        st[si] = merge(st[si * 2 + 1], st[si * 2 + 2]);
    }
    // Function to construct segment tree from given string.
    // This function allocates memory for segment tree and calls
    // constructSTUtil() to fill the allocated memory
    public void constructST(string str) {
          // Allocate memory for segment tree
        // Height of segment tree
        int x = (int) Math.Ceiling(Math.Log(n) / Math.Log(2));
         // Maximum size of segment tree
        int max_size = 2 * (int) Math.Pow(2, x) - 1;
 
        st = new Node[max_size];
        for (int i = 0; i < max_size; i++)
            st[i] = new Node();
  // Fill the allocated memory st
        constructSTUtil(str, 0, n - 1, 0);
    }
    // A Recursive function to get the desired Maximum Sum Sub-Array,
    // The following are parameters of the function-
    // st     --> Pointer to segment tree
    // si --> Index of the segment tree Node
    // ss & se --> Starting and ending indexes of the segment
    // represented by current Node, i.e., tree[index]
    // qs & qe --> Starting and ending indexes of query range
    Node queryUtil(int ss, int se, int qs, int qe, int si) {
        // No overlap
        if (ss > qe || se < qs)
         // returns a Node for out of bounds condition
            return new Node();
 
        if (ss >= qs && se <= qe)
            return st[si];
 
        int mid = getMid(ss, se);
        Node left = queryUtil(ss, mid, qs, qe, si * 2 + 1);
        Node right = queryUtil(mid + 1, se, qs, qe, si * 2 + 2);
        return merge(left, right);
    }
  
      
    // The function to get the maximum length of correct bracket subsequence
    // for given range. The following are parameters of the function-
    // st --> Pointer to segment tree
    // qs & qe --> Starting and ending indexes of query range
    public int query(int qs, int qe) {
         
        Node node = queryUtil(0, n - 1, qs, qe, 0);
        return 2 * node.pairs;
    }
//Driver code
    public static void Main(string[] args) {
        string str = "())(())(())(";
        int n = str.Length;
        SegmentTree tree = new SegmentTree();
        tree.n = n;
        tree.constructST(str);
        int qs = 0;
        int qe = n - 1;
        Console.WriteLine("Maximum Length Correct Bracket Subsequence between " + qs + " and " + qe + " = " + tree.query(qs, qe));
         
        qs = 1;
        qe = 2;
        Console.WriteLine("Maximum Length Correct Bracket Subsequence between " + qs + " and " + qe + " = " + tree.query(qs, qe));
    }
}


Output

Maximum Length Correct Bracket Subsequence between 0 and 11 = 10
Maximum Length Correct Bracket Subsequence between 1 and 2 = 0

Time complexity: O(N*log N), where N is the size of the string
Auxiliary Space: O(N)



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads