📜  最长正确的括号子序列的范围查询

📅  最后修改于: 2021-04-17 08:23:44             🧑  作者: Mango

给定括号序列,换句话说,字符串S的长度为n,由字符“(”和“)”组成。查找给定查询范围的序列的最大正确括号子序列的长度。注意:正确的括号序列是具有匹配的括号对或包含另一个嵌套的正确括号序列的序列。例如,(),(()),()()是一些正确的括号序列。

例子:

Input : S = ())(())(())(
        Start Index of Range = 0, 
        End Index of Range = 11
Output : 10
Explanation:  Longest Correct Bracket Subsequence is ()(())(())

Input : S = ())(())(())(
        Start Index of Range = 1, 
        End Index of Range = 2
Output : 0

段树可以用来有效地解决这个问题

在段树的每个节点上,我们存储以下内容:

1) a - Number of correctly matched pairs of brackets.
2) b - Number of unused open brackets.  
3) c - Number of unused closed brackets.

(未使用的开放式括号–表示它们不能与任何封闭式括号匹配,未使用的封闭式括号–表示它们无法与任何开放式括号匹配,例如S =)(包含未使用的开放式括号和未使用的封闭式括号)

对于每个间隔[L,R],我们可以将间隔[MID + 1,R]中X个未使用的方括号’(’与间隔[L,MID]中的未使用方括号’)’相匹配,其中
X =最小值([L,MID]中未使用的’(’的数量,[MID + 1,R]中未使用的’)’的数量)
因此,X也是通过组合建立的正确匹配对的数量。
因此,对于间隔[L,R]

1)正确匹配对的总数成为左子项中正确匹配的对与右子项中正确匹配的对的总和,分别来自左子项和右子项的未使用的’(’和未使用的’)’的组合数。

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

2)未使用的方括号总数等于左子项中未使用的方括号和右子项中未使用的方括号的总和减去X(减–因为我们使用X未使用的’(’来自左子项的’(’)与未使用的’)匹配右子项的未使用’ )。

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

3)同样,对于无括号的方括号,遵循以下关系成立。

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

其中a,b和c是上述要存储的每个节点的表示。

下面是上述方法在C++中的实现。

/* CPP Program to find the longest correct
   bracket subsequence in a given range */
#include 
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);
  
    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;
}
输出:
Maximum Length Correct Bracket Subsequence between 0 and 11 = 10
Maximum Length Correct Bracket Subsequence between 1 and 2 = 0

每个查询的时间复杂度为O(logN) ,其中N是字符串的大小。