自动生成括号 括号匹配 最长有效括号

  近几日,在刷力扣时,看到 最长有效括号这一题(后文会给出解答),便想写一个自动生成括号组合并判断是否匹配的程序。

一、自动生成括号及括号匹配

  首先,我们要解决的是括号生成问题,当然,我们可以利用回溯法直接写出符合匹配规则的括号组合,但这样便没有判断是否匹配的必要,这不符合我的本意。
  在括号生成中,先画出回溯树,在本问题中,就不对回溯树进行剪枝。不难发现,在每个可选择的结点中,都面临两种选择,选’(’ 或 ‘)’。该回溯树的终点就是路径的长度等于需要生成括号的个数,下面来看一下生成括号的程序。

public static LinkedList<String> list = new LinkedList();
public static void partition(int start, int len, StringBuilder sb){
    if(start == len){
        list.add(sb.toString());
        return;
    }
    sb.append("(");
    partition(start + 1, len, sb);
    sb.deleteCharAt(sb.length() - 1);

    sb.append(")");
    partition(start + 1, len, sb);
    sb.deleteCharAt(sb.length() - 1);
}

运行程序,调用 partition(0,3,new StringBuilder(“”));得到的结果如下图所示:
在这里插入图片描述
现在,我们已经解决了第一个问题,在判断括号是否匹配时,可以借助栈这一数据结构来实现。
栈不同于队列的先进先出,而是先进后出,使用压栈出栈的方式来解决各种问题。
在本问题中,如何使用栈来解决呢。首先,我们判断栈为空还是非空

  • 如果为空:这说明,目前还没有进行括号匹配,或者前面的元素已经一一匹配,符合条件。
    • 如果没有进行括号匹配,判断入栈的元素是否为’(‘,如果是,可以入栈,反之,直接返回不匹配,因为,’(‘总是出现在’)‘之前,如果直接出现’)',与事实不符。
    • 如果前面的元素已经一一匹配,还是判断判断入栈的元素是否为’(',分析与上文一致,只不过前提条件不一样。在代码中,并没有体现二者的不同,这里只是为了让大家可以更好地理解。
  • 如果是非空:
    • 判断栈顶数据和要入栈的数据是否匹配,若匹配,则出栈,并把继续判断下一个元素。若不匹配,把数据入栈。

最后,遍历完所有数据之后,判断栈是否为空即可得出答案。代码如下:

public static boolean judgement(String s, int left, int right){
    if(left == right || s.charAt(left)== ')'|| right - left + 1 % 2 == 1)
        return false;

    Stack<Character> stack = new Stack();
    stack.push(s.charAt(left));
    int index = left + 1;
    while (index <= right ){
        if(stack.isEmpty()){
            //栈为空
            if(s.charAt(index) == ')')
                return false;
            else
                stack.push('(');
            index++;
        }else {
            //栈不为空,判断栈顶和入栈的元素是否匹配,若不匹配则入栈
            char down = stack.peek();
            char up = s.charAt(index);
            if(down == '(' && up == ')'){
                stack.pop();
                index++;
            }else {
                stack.push(s.charAt(index));
                index++;
            }
        }
    }
    return stack.isEmpty();
}

最终,把两个部分的代码汇总,就可以得到整个程序,完整代码如下(直接生成符合匹配条件括号程序也会在下文给出)

import java.util.LinkedList;
import java.util.Stack;

public class w0403_study {
    public static LinkedList<String> list = new LinkedList();

    public static void main(String[] args) {
        partition(0,3,new StringBuilder(""));
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i)+"   括号是否匹配?  "+judgement(list.get(i), 0, list.get(i).length() - 1));
        }
    }
    public static void partition(int start, int len, StringBuilder sb){
        if(start == len){
            list.add(sb.toString());
            return;
        }
        sb.append("(");
        partition(start+1,len,sb);
        sb.deleteCharAt(sb.length() - 1);

        sb.append(")");
        partition(start+1,len,sb);
        sb.deleteCharAt(sb.length() - 1);
    }

    public static boolean judgement(String s, int left, int right){
        if(left == right || s.charAt(left)== ')'|| right - left + 1 % 2 == 1)
            return false;

        Stack<Character> stack = new Stack();
        stack.push(s.charAt(left));
        int index = left + 1;
        while (index <= right ){
            if(stack.isEmpty()){
                //栈为空
                if(s.charAt(index) == ')')
                    return false;
                else
                    stack.push('(');
                index++;
            }else {
                //栈不为空,判断栈顶和入栈的元素是否匹配,若不匹配则入栈
                char down = stack.peek();
                char up = s.charAt(index);
                if(down == '(' && up == ')'){
                    stack.pop();
                    index++;
                }else {
                    stack.push(s.charAt(index));
                    index++;
                }
            }
        }
        return stack.isEmpty();
    }
}

运行结果如下图所示:
在这里插入图片描述

二、自动生成匹配括号

题目:请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 :

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]

与上文不同的是:
本题需要直接实现括号生成程序。在这里,又加入了两个新的参数,left和right,即剩余左括号和右括号的个数。
在括号匹配的规则中,已经生成的括号,左括号的个数必须要大于等于右括号的个数,这里大家需要理解一下,这是剪枝的一个条件。
终止条件就是左括号和右括号都被用完,即left = 0,right = 0。
当然,在left或right小于01时,显然与事实不符,直接return;

public List<String> str = new LinkedList<>();

public List<String> generateParenthesis(int n) {
    generate_parenthesis(new StringBuilder(""), n, n);
    return str;
}

public void generate_parenthesis(StringBuilder sb, int left, int right) {
    if (left == 0 && right == 0) {
        str.add(sb.toString());
        return;
    }
    if (left < 0 || right < 0)
        return;

    if (left > right)
        return;

    sb.append("(");
    generate_parenthesis(sb, left - 1, right);
    sb.deleteCharAt(sb.length() - 1);

    sb.append(")");
    generate_parenthesis(sb, left, right - 1);
    sb.deleteCharAt(sb.length() - 1);
}

测试:当left = right = 3时,得到的结果如下图所示
在这里插入图片描述

三、最长有效括号

给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

示例 1:
输入:s = “(()”
输出:2
解释:最长有效括号子串是 “()”

示例 2:
输入:s = “)()())”
输出:4
解释:最长有效括号子串是 “()()”

示例 3:
输入:s = “”
输出:0

思路

定义 d p [ i ] dp[i] dp[i] 为以下标 i i i 结尾的最长有效括号的长度。在遍历时,我们注意到以’(‘结尾的子串的有效长度为0,所以只需要计算以’)'结尾的子串的有效长度。

s [ i ] s[i] s[i] 为’('时, d p [ i ] = 0 dp[i] = 0 dp[i]=0

s [ i ] s[i] s[i] 为’)'时,

  • 如果 s [ i − 1 ] s[i-1] s[i1] 为’(', d p [ i ] = d p [ i − 2 ] + 2 dp[i] = dp[i-2]+2 dp[i]=dp[i2]+2
  • 如果 s [ i − 1 ] s[i-1] s[i1] 为’)‘,并且 s [ i − d p [ i − 1 ] − 1 ] s[i - dp[i-1]-1] s[idp[i1]1] 为’(', d p [ i ] = d p [ i − 1 ] + 2 + d p [ i − d p [ i − 1 ] − 2 ] dp[i] =dp[i-1]+2+dp[i-dp[i-1]-2] dp[i]=dp[i1]+2+dp[idp[i1]2]

最终返回的长度就是 d p dp dp 数组中的最大值。

public int longestValidParentheses(String s) {
    int dp[] = new int[s.length()];
    int max = 0;
    for (int i = 1; i < dp.length; i++) {
        if(s.charAt(i) == '(') {
            dp[i] = 0;
        } else {
          if(s.charAt(i - 1) == '(') {
              if(i - 2 > 0){
                  dp[i] = dp[i - 2] + 2;
              }else {
                  dp[i] = 2;
              }
          }else {
              if (i - dp[i - 1] - 1 >= 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
                  if (i - dp[i - 1] - 2 >= 0) {
                      dp[i] = dp[i - 1] + 2 + dp[i - dp[i - 1] - 2];
                  } else {
                      dp[i] = dp[i - 1] + 2;
                  }
              }
          }
        }
        max = Math.max(max, dp[i]);
    }
    return max;
}

当然,大家也可以尝试使用双重for循环,加上上文中的判别括号是否匹配的函数,也能解决此问题,但是这种方法的复杂度非常高,不建议使用。
ps:后续会更新大量有关回溯、动态规划、dfs、bfs的刷题blog
尽情期待,未完待续…
看到这的小伙伴不妨留下一个免费的赞吧。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值