leetcode20、22——有效括号问题(判断有效的括号以及生成有效的括号)

1、判断有效的括号

1.1、题目

题目链接
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。

有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。

注意空字符串可被认为是有效字符串。

输入: "()"
输出: true

输入: "()[]{}"
输出: true

输入: "(]"
输出: false

输入: "([)]"
输出: false

1.2、思路

核心思路:,首先我们应该明确如下两条规则:

  • 1、一个「合法」括号组合的左括号数量一定等于右括号数量
  • 2、对于一个「合法」的括号字符串组合 p,必然对于任何 0 <= i < len ( p) 都有:子串 p[0..i]左括号的数量大于或等于右括号的数量
    • 因为从左往右算的话,肯定是左括号多嘛,到最后左右括号数量相等

我们先处理一种括号:

  • 每个右括号 ) 的左边必须有一个左括号 ( 和它匹配
bool isValid(string str) {
    // 待匹配的左括号数量
    int left = 0;
    for (char c : str) {
        if (c == '(')
            left++;
        else // 遇到右括号
            left--;

        if (left < 0)
            return false;
    }
    return left == 0;
}

对于三种括号的情况,用一个名为 left 的代替之前思路中的 left 变量,遇到左括号就入栈遇到右括号就去栈中寻找最近的左括号,看是否匹配。

1.3、题解

class Solution {
public:
    bool isValid(string s) {
        int len = s.size();
        if(len % 2 != 0)
            return false;
        stack<char>stack;
        map<char,char>m = {{')','('},{']','['},{'}','{'}};//注意这里面括号的顺序,根据右侧括号,找左侧
        for(int i = 0;i < len ;i++)
        {
            if(s[i] == '(' || s[i] == '[' || s[i] == '{')
                stack.push(s[i]);//遇到左括号就push
            else
            {
                if(stack.empty())
                    return false;
                if(m[s[i]] == stack.top())
                    stack.pop();//遇到右括号就pop
                else
                    return false;
            }
        }
        return stack.empty();
    }
};

2、生成有效的括号

2.1、题目

题目链接
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合

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

2.2、思路

对于括号合法性的判断,主要是借助「栈」这种数据结构,而对于括号的生成,一般都要利用回溯递归的思想。

重复两条规则:

  • 1、一个「合法」括号组合的左括号数量一定等于右括号数量
  • 2、对于一个「合法」的括号字符串组合 p,必然对于任何 0 <= i < len ( p) 都有:子串 p[0..i]左括号的数量大于或等于右括号的数量
    • 因为从左往右算的话,肯定是左括号多嘛,到最后左右括号数量相等

对于 2n 个位置,必然有 n 个左括号n 个右括号,用 left 记录还可以使用多少个左括号,用 right 记录还可以使用多少个右括号

2.3、题解

vector<string> generateParenthesis(int n) {
    if (n == 0) return {};
    // 记录所有合法的括号组合
    vector<string> res;
    // 回溯过程中的路径
    string track;
    // 可用的左括号和右括号数量初始化为 n
    backtrack(n, n, track, res);
    return res;
}

// 可用的左括号数量为 left 个,可用的右括号数量为 rgiht 个
void backtrack(int left, int right, 
            string& track, vector<string>& res) {
    // 若左括号剩下的多,说明不合法。剩的多因为用的少嘛,左侧先填充的,所以就这样了。
    if (right < left) return;
    // 数量小于 0 肯定是不合法的
    if (left < 0 || right < 0) return;
    // 当所有括号都恰好用完时,得到一个合法的括号组合
    if (left == 0 && right == 0) {
        res.push_back(track);
        return;
    }

    // 尝试放一个左括号
    track.push_back('('); // 选择
    backtrack(left - 1, right, track, res);
    track.pop_back(); // 撤消选择

    // 尝试放一个右括号
    track.push_back(')'); // 选择
    backtrack(left, right - 1, track, res);
    track.pop_back();// 撤消选择
}

3、最长有效括号

3.1、题目

题目链接

给定一个只包含 '('')' 的字符串,找出最长的包含有效括号的子串的长度。

示例 1:

输入: "(()"
输出: 2
解释: 最长有效括号子串为 "()"
示例 2:

输入: ")()())"
输出: 4
解释: 最长有效括号子串为 "()()"

3.2、思路

状态

  • dp[i] 表示以下标 i 为字符结尾的最长有效字符串的长度

状态转移方程

  • 以 ( 结尾的子字符串不考虑,因为不可能构成合法括号

if s[i] == ‘)’

  • s[i - 1] == ‘(’,也就是字符串形如 “……()”,我们可以推出:dp[i] = dp[i − 2] + 2。
  • 因为结束部分的 “()” 是一个有效子字符串,并且将之前有效子字符串的长度增加了 2.

s[i - 1] == ‘)’,也就是字符串形如 “…))”,我们可以推出:

if s[i - dp[i - 1] - 1] == '(',
dp[i] = dp[i − 1] + dp[i − dp[i − 1]2] + 2

因为如果倒数第二个 )是一个有效子字符串的一部分(记为subs),我们此时需要判断 subs 前面一个符号是不是 (如果恰好是(,我们就用 subs 的长度(dp[i - 1)加上 2 去更新 dp[i]

除此以外,我们也会把子字符串 subs 前面的有效字符串的长度加上,也就是 dp[i − dp[i − 1] − 2].

3.3、题解

暴力法

bool isVaild(string s) {
    stack<char> a;
    for (auto v : s) {
        if (v == '(') {
            a.push(v);
        } else if (!a.empty() && a.top() == '(') {
            a.pop();
        } else return false;
    }
    return a.empty();
}

int longestValidParentheses(string s) {
    int maxLen = 0;
    for (int i = 0; i < s.length(); i++) {
        for (int j = 2; j + i <= s.length(); j += 2) {
            if (isVaild(s.substr(i, j))) {
                if (j > maxLen) {
                    maxLen = j;
                }
            }
        }
    }
    return maxLen;
}

class Solution {
public:
    int longestValidParentheses(string s) {
        int size = s.length();
        vector<int> dp(size, 0);

        int maxVal = 0;
        for(int i = 1; i < size; i++) {
            if (s[i] == ')') {
                if (s[i - 1] == '(') {
                    dp[i] = 2;
                    if (i - 2 >= 0) {
                        dp[i] = dp[i] + dp[i - 2];
                    }
                } else if (dp[i - 1] > 0) {
                    if ((i - dp[i - 1] - 1) >= 0 && s[i - dp[i - 1] - 1] == '(') {
                        dp[i] = dp[i - 1] + 2;
                        if ((i - dp[i - 1] - 2) >= 0) {
                            dp[i] = dp[i] + dp[i - dp[i - 1] - 2];
                        }
                    }
                }
            }
            maxVal = max(maxVal, dp[i]);
        }
        return maxVal;
    }
};

复杂度分析:

时间复杂度:O(n)。遍历整个字符串一次,就可以将 dp 数组求出来.
空间复杂度: O(n)。需要一个大小为 n 的 dp 数组.

参考

https://labuladong.gitbook.io/algo/suan-fa-si-wei-xi-lie/he-fa-kuo-hao-sheng-cheng

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值