LeetCode之字符串系列:有效的括号(匹配),有效的括号字符串,括号生成,最长有效的括号(困难)

20. 有效的括号

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

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

示例 1:
输入:s = “()”
输出:true

示例 2:
输入:s = “()[]{}”
输出:true

示例 3:
输入:s = “(]”
输出:false

示例 4:
输入:s = “([)]”
输出:false

示例 5:
输入:s = “{[]}”
输出:true

提示:
1 <= s.length <= 104
s 仅由括号 ‘()[]{}’ 组成

栈匹配

算法原理

  • 栈先入后出特点恰好与本题括号排序特点一致,即若遇到左括号入栈,遇到右括号时将对应栈顶左括号出栈,则遍历完所有括号后 stack 仍然为空;
  • 建立哈希表 dic 构建左右括号对应关系:key括号,value右括号;这样查询 2 个括号是否对应只需 O(1) 时间复杂度;建立栈 stack,遍历字符串 s 并按照算法流程一一判断。

算法流程

  • 如果 c 是左括号,则入栈 push;否则通过哈希表判断括号对应关系,若 stack 栈顶出栈括号 stack.pop() 与当前遍历括号 c 不对应,则提前返回 false。
  • 提前返回优点: 在迭代过程中,提前发现不符合的括号并且返回,提升算法效率。
  • 解决边界问题:
    • 栈 stack 为空: 此时 stack.pop() 操作会报错;因此,我们采用一个取巧方法,给 stack 赋初值 ?? ,并在哈希表 dic 中建立 key: ‘?’,value:’? 的对应关系予以配合。此时当 stack 为空且 c 为右括号时,可以正常提前返回 false;
    • 字符串 s 以左括号结尾: 此情况下可以正常遍历完整个 s,但 stack 中遗留未出栈的左括号;因此,最后需返回 len(stack) == 1,以判断是否是有效的括号组合。

复杂度分析

  • 时间复杂度 O(N):正确的括号组合需要遍历 1遍 s;
  • 空间复杂度 O(N):哈希表和栈使用线性的空间大小。
class Solution:
    def isValid(self, s: str) -> bool:
        if len(s) % 2 == 1:
            return False
        d = {'{':'}','(':')','[':']','?': '?'}
        stack =['?']
        for c in s:
            if c in d:
                stack.append(c)
            elif d[stack.pop()] != c:
                return False
        return len(stack)==1
678. 有效的括号字符串

给定一个只包含三种字符的字符串:( ,) 和 *,写一个函数来检验这个字符串是否为有效字符串。有效字符串具有如下规则:

  • 任何左括号 ( 必须有相应的右括号 )。
  • 任何右括号 ) 必须有相应的左括号 ( 。
  • 左括号 ( 必须在对应的右括号之前 )。
  • ∗ * 可以被视为单个右括号 ) ,或单个左括号 ( ,或一个空字符串。
    一个空字符串也被视为有效字符串。

示例 1:
输入: “()”
输出: True

示例 2:
输入: “(*)”
输出: True

示例 3:
输入: “(*))”
输出: True
注意:

  • 字符串大小将在 [1,100] 范围内。
解题思路:贪心算法
  1. 用一个变量delta维护当前左括号的数量,左括号+1、右括号-1:

  2. 如果任意时刻有delta < 0说明右括号比左括号多,直接失败

  3. 如果最终有delta == 0说明平衡,才成功。

  4. 这里由于 ∗ * 可以变成3种字符,如果用贪心法也是可以的:维护当前左括号的数量范围[L, R],遇到(则同时加1、遇到)则同时减1、遇到*则扩展范围(–L, ++R),同理:

  5. 如果任意时刻有R < 0,则失败

  6. 如果最后有L <= 0 <= R,才成功因为加1、减1的过程会保证[L, R]是连续整数区间,所以我们只需要维护区间的左右边界,而不需要维护该区间中的所有数字。

class Solution {
public:
    bool checkValidString(string s) {
        int min = 0, max = 0; // 维护当前左括号的数量范围:[min, max]
        for (auto c : s) {
        if (c == '(') {
            ++min;
            ++max;
        } else if (c == ')') {
            if (min > 0) min--;
            if (max-- == 0) return false;// 左括号不够
        } else {
            if (min > 0) min--; // 可作为右括号,抵消
            ++max; // 可作为左括号
        }
    }
    return min == 0;
    }
};
class Solution:
    def checkValidString(self, s: str) -> bool:
        L,R=0,0
        for ch in s:
            if ch == '(':
                L += 1
                R += 1
            elif ch == ')':
                if L > 0:
                    L -= 1
                if R > 0:
                    R -= 1
                else:
                    return False
            else:
                if L > 0:
                    L -= 1
                R += 1
        return L==0
22. 括号生成

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

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

示例 2:
输入:n = 1
输出:["()"]

提示:
1 <= n <= 8

动态规划与回溯法

- 回溯法

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        ans = []
        def backtrack(S, left, right):
            if len(S) == 2 * n:
                ans.append(''.join(S))
                return
            if left < n:
                S.append('(')
                backtrack(S, left+1, right)
                S.pop()
            if right < left:
                S.append(')')
                backtrack(S, left, right+1)
                S.pop()

        backtrack([], 0, 0)
        return ans

- 动态规划

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        res = []
        cur_str = ''

        def dfs(cur_str, left, right, n):
            """
            :param cur_str: 从根结点到叶子结点的路径字符串
            :param left: 左括号已经使用的个数
            :param right: 右括号已经使用的个数
            :return:
            """
            if left == n and right == n:
                res.append(cur_str)
                return
            if left < right:
                return

            if left < n:
                dfs(cur_str + '(', left + 1, right, n)

            if right < n:
                dfs(cur_str + ')', left, right + 1, n)

        dfs(cur_str, 0, 0, n)
        return res
最长有效的括号(困难)

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

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

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

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

提示:
0 <= s.length <= 3 * 104
s[i] 为 ‘(’ 或 ‘)’

  1. 对于遇到的每个 ‘(’ ,我们将它的下标放入栈中。
  2. 对于遇到的每个 ‘)’ ,我们先弹出栈顶元素表示匹配了当前右括号。
  3. 如果栈为空,说明当前的右括号为没有被匹配的右括号,我们将其下标放入栈中来更新我们之前提到的「最后一个没有被匹配的右括号的下标」。
  4. 如果栈不为空,当前右括号的下标减去栈顶元素即为「以该右括号为结尾的最长有效括号的长度」。
  5. 我们从前往后遍历字符串并更新答案即可。
class Solution:
    def longestValidParentheses(self, s: str) -> int:
        if not s:
            return 0
        res = 0
        stack = [-1]
        for i in range(len(s)):
            if s[i] == "(":
                stack.append(i)
            else:
                stack.pop()
                if not stack:
                    stack.append(i)
                else:
                    res = max(res,i - stack[-1])
        return res

复杂度分析

  • 时间复杂度: O(n),n是给定字符串的长度。我们只需要遍历字符串一次即可。

  • 空间复杂度: O(n)。栈的大小在最坏情况下会达到 n,因此空间复杂度为O(n) 。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值