How to solve bracket related problems

Keywords: Algorithm

https://labuladong.gitee.io/algo/4/32/135/

 

After reading this article, you can not only learn the algorithm routine, but also win the following topics on LeetCode:

20. Valid parentheses (simple)

921. Minimum addition to make parentheses valid (medium)

1541. Minimum insertion of balanced bracket string (medium)

-—–

Judge legal bracket string

The legitimacy judgment of parentheses has appeared many times in the written test, which is also very common in reality. For example, in the code we write, the editor will check whether the parentheses are closed correctly. And our code may contain three kinds of parentheses   [] () {}, it's a little difficult to judge.

Take a look at question 20 "valid parentheses" of Li Kou. Enter a string containing   [](){}   Six kinds of parentheses. Please judge whether the parentheses composed of this string are legal.

For example:

Input: "()[]{}"
Output: true

Input: "([)]"
Output: false

Input: "{[]}"
Output: true

Before we solve this problem, let's reduce the difficulty and think about if there is only one bracket   (), how to judge whether the parentheses composed of strings are legal?

Assuming that there are only parentheses in the string, if you want the parenthesis string to be legal, you must:

Each closing bracket  )  Must have an open parenthesis to the left of   (   Match it.

For example, string   ()))((   In, there is no left parenthesis matching to the left of the two right parentheses in the middle, so this parenthesis combination is illegal.

Then, according to this idea, we can write the algorithm:

bool isValid(string str) {
    // Number of left parentheses to match
    int left = 0;
    for (int i = 0; i < str.size(); i++) {
        if (s[i] == '(') {
            left++;
        } else {
            // Right parenthesis encountered
            left--;
        }

        // Too many closing parentheses
        if (left == -1)
            return false;
    }
    // Are all the left parentheses matched
    return left == 0;
}

If there are only parentheses, we can correctly judge the legitimacy. For the case of three parentheses, I wanted to imitate this idea and define three variables at the beginning   left1,left2,left3   Handle each kind of parenthesis separately. Although it needs to write a lot of if else branches, it seems to solve the problem.

But in fact, it is impossible to copy this idea directly, for example, when there is only one bracket   (())   It's legal, but in the case of multiple parentheses,   [(])   Obviously illegal.

Just recording the number of occurrences of each left parenthesis can no longer make a correct judgment. We need to increase the amount of information stored. We can use stack to imitate similar ideas. Stack is a first in and last out data structure, which is particularly useful when dealing with parenthesis problems.

We use a name for this problem   left   Instead of the stack in the previous idea   left   Variable, enter the stack when encountering the left bracket, and look for the nearest left bracket in the stack when encountering the right bracket to see whether it matches:

bool isValid(string str) {
    stack<char> left;
    for (char c : str) {
        if (c == '(' || c == '{' || c == '[')
            left.push(c);
        else { // The character c is a closing parenthesis
            if (!left.empty() && leftOf(c) == left.top())
                left.pop();
            else
                // Does not match the nearest left parenthesis
                return false;
        }
    }
    // Are all the left parentheses matched
    return left.empty();
}

char leftOf(char c) {
    if (c == '}') return '{';
    if (c == ')') return '(';
    return '[';
}

Next, let's talk about two other common questions, how to make parentheses legal through the minimum number of inserts?

Balanced bracket string (I)

Let's start with a simple question 921 "minimum addition of effective parentheses":

Enter a string for you   s. You can insert an open parenthesis anywhere in it   (   Or right parenthesis  ), How many times do you need to insert at least to make it   s   Become a legal string of parentheses?

For example, input   S = "()) (", the algorithm should return 2, because we need to insert it at least twice   s   become   "(()) ()", so that each left parenthesis has a right parenthesis to match, S   Is a valid string of parentheses.

In fact, this is very similar to the above judgment on the legitimacy of parentheses. Let's look at the code directly:

int minAddToMakeValid(string s) {
    // res record insertion times
    int res = 0;
    // The need variable records the demand in the right bracket
    int need = 0;

    for (int i = 0; i < s.size(); i++) {
        if (s[i] == '(') {
            // Requirement for right parenthesis + 1
            need++;
        }
        
        if (s[i] == ')') {
            // Requirements for closing parentheses - 1
            need--;

            if (need == -1) {
                need = 0;
                // An open parenthesis needs to be inserted
                res++;
            }
        }
    }
    
    return res + need;
}

This code is the final solution. The core idea is to maintain the number of requirements for the right bracket based on the left bracket   Need to calculate the minimum number of inserts. Two points need to be noted:

1. When   need == -1   What does it mean when you are?

Because only right parentheses are encountered  )  Only when   need--,need == -1   It means that there are too many right parentheses, so you need to insert the left parenthesis.

for instance   s = "))"   In this case, two left parentheses need to be inserted so that   s   become   "() ()", is a valid string of parentheses.

2. Why does the algorithm return   res + need?

because   res   Record the insertion times of the left parenthesis, need   The requirement in the right bracket is recorded. When the for loop ends, if   need   If it is not 0, it means that the right bracket is not enough and needs to be inserted.

for instance   s = "))("   In this case, after inserting two left parentheses, insert another right parenthesis so that   s   become   "()", is a valid string of parentheses.

The above is the idea of this problem. Next, let's look at an advanced problem. What will happen if the left and right brackets are not paired 1:1?

Balanced bracket string (II)

This is question 1541 "minimum number of insertion of balanced bracket string":

Now suppose that a left bracket needs to match two right brackets to be called a legal bracket combination, then enter a bracket string for you   s. How do you calculate   s   What is the legal minimum number of inserts?

The core idea is the same as just now, through a   need   The variable records the number of requirements for the right parenthesis according to   need   To determine whether insertion is required.

The first step is to maintain it correctly according to the idea just now   need   Variable:

int minInsertions(string s) {
    // need records the demand quantity in the right bracket
    int res = 0, need = 0;
    
    for (int i = 0; i < s.size(); i++) {
        // One left bracket corresponds to two right brackets
        if (s[i] == '(') {
            need += 2;
        }
        
        if (s[i] == ')') {
            need--;
        }
    }
    
    return res + need;
}

Now think about it, when   need   Why can we determine the need for insertion when the value is?

First, similar to the first question, when   need == -1   It means that we encounter an extra right parenthesis and obviously need to insert an left parenthesis.

For example, when   s = ")", we definitely need to insert an open parenthesis to let   s = "()", but since one left bracket requires two right brackets, the demand for the right bracket becomes 1:

if (s[i] == ')') {
    need--;
    // There are too many closing parentheses
    if (need == -1) {
        // An open parenthesis needs to be inserted
        res++;
        // At the same time, the requirement for the right bracket becomes 1
        need = 1;
    }
}

In addition, when the left bracket is encountered, if the demand for the right bracket is odd, one right bracket needs to be inserted. Because a left bracket needs two right brackets, the requirement of the right bracket must be even, which is also the difficulty of this problem.

Therefore, when encountering the left bracket, make the following judgment:

if (s[i] == '(') {
    need += 2;
    if (need % 2 == 1) {
        // Insert a closing bracket
        res++;
        // Demand for right parentheses minus one
        need--;
    }
}

To sum up, we can write the correct code:

int minInsertions(string s) {
    int res = 0, need = 0;

    for (int i = 0; i < s.size(); i++) {
        if (s[i] == '(') {
            need += 2;
            if (need % 2 == 1) {
                res++;
                need--;
            }
        }
        
        if (s[i] == ')') {
            need--;
            if (need == -1) {
                res++;
                need = 1;
            }
        }
    }
    
    return res + need;
}

To sum up, the problems related to the three brackets are solved. In fact, we mentioned earlier   Legitimate bracket generation algorithm   It is also a bracket related problem, but the backtracking algorithm skills used are quite different from several questions in this paper. Interested readers can go and have a look.

Posted by Begby on Thu, 11 Nov 2021 10:42:52 -0800