leetcode-括号问题

本文探讨了如何通过贪心、回溯和记忆化搜索算法解决三个与括号相关的问题:检查括号的有效性、路径中合法括号串的存在和删除无效括号。作者分享了多种实现方法,包括计数、栈操作和回溯策略的运用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

678. 有效的括号字符串

 贪心

只有左右括号,本质上还根据

最后左括号数量=右括号数量,且遍历到当前下标时,左括号数量一定得大于或者等于右括号数量

力扣

class Solution {

    public boolean checkValidString(String s) {
        
        int min = 0, max = 0; // 维护当前左括号的数量范围:[min, max]
        char[] chars = s.toCharArray();
        for(char i : chars) {
            if(i == '(') {
                min++;
                max++;
            }else if(i == ')') {
                //min > 0才--
                if(min > 0) min--;
                if(max-- == 0) return false;
            }else {
                //min > 0才--
                if(min > 0) min--;
                max++;
            }
        }
        return min == 0;
    }

}


作者:codeppx
链接:https://leetcode.cn/problems/valid-parenthesis-string/solution/codepi-pi-xia-shuang-zhan-tan-xin-si-lu-bua32/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

6059. 检查是否有合法括号字符串路径

 回溯:超时

class Solution(object):
    def hasValidPath(self, grid):
        """
        :type grid: List[List[str]]
        :rtype: bool
        """
        def check(alist):
            stack = [alist[0]]
            for i in range(1, len(alist)):
                if stack and alist[i] == stack[-1]:
                    stack.append(alist[i])
                else:
                    if stack:
                        stack.pop()
                    else:
                        stack.append(alist[i])
            if len(stack) == 0:
                return True
            return False
        directions = [(1,0),(0,1)]
        def dfs(start_x,start_y,left,right):
            if right > left:
                return False
            if start_x == len(grid)-1 and start_y == len(grid[0])-1:
                print(path)
                if check(path):
                    return True
                else:
                    return False
            for direction in directions:
                new_x = start_x + direction[0]
                new_y = start_y + direction[1]
                if new_x >= 0 and new_x <=len(grid)-1 and new_y >=0 and new_y <=len(grid[0])-1:
                    path.append(grid[new_x][new_y])
                    if grid[new_x][new_y] == "(":
                        left += 1
                    if grid[new_x][new_y] == ")":
                        right += 1
                    if dfs(new_x,new_y,left,right):
                        return True
                    path.pop()
            return False
        #特殊情况
        path = []
        path.append(grid[0][0])
        if grid[0][0] == "(":
            return dfs(0,0,1,0)
        if grid[0][0] == ")":
            return False

回溯+括号判断

只要不是带有list的回溯,是可以用lru_cache的。

注意一定要回溯,因为是在for循环里对值进行加减的

for direction in directions:
                new_x = start_x + direction[0]
                new_y = start_y + direction[1]
                if new_x >= 0 and new_x <= len(grid) - 1 and new_y >= 0 and new_y <= len(grid[0]) - 1:
                    if grid[new_x][new_y] == "(":
                        left_count += 1
                    if grid[new_x][new_y] == ")":
                        right_count += 1

不回溯的话for循环会对left_count和right_count进行覆盖。

class Solution:
    def hasValidPath(self, grid: List[List[str]]) -> bool:
        directions = [(1,0),(0,1)]
        @lru_cache(None)
        def dfs(start_x,start_y,left_count,right_count):
            if right_count > left_count:
                return False
            if start_x == len(grid) - 1 and start_y == len(grid[0]) - 1:
                if left_count == right_count:
                    return True
                else:
                    return False
            for direction in directions:
                new_x = start_x + direction[0]
                new_y = start_y + direction[1]
                if new_x >= 0 and new_x <= len(grid) - 1 and new_y >= 0 and new_y <= len(grid[0]) - 1:
                    if grid[new_x][new_y] == "(":
                        left_count += 1
                    if grid[new_x][new_y] == ")":
                        right_count += 1
                    if dfs(new_x, new_y, left_count,right_count):
                        return True
                    if grid[new_x][new_y] == "(":
                        left_count -= 1
                    if grid[new_x][new_y] == ")":
                        right_count -= 1
            return False
        if grid[0][0] == ")":
            return False
        return dfs(0,0,1,0)

记忆化深搜+括号判断

手把手解决三道括号相关的算法题

只有圆括号的情况下,不必用栈。判断方法是

左括号的数量最后与右括号相等,并且任意位置左括号数量一定不小于右括号数量。

 不用回溯写法的话,不能在for循环里改变dfs的参数值

class Solution:
    def hasValidPath(self, grid: List[List[str]]) -> bool:
        @lru_cache(None)
        def dfs(i, j, c):
            if c < 0 or i == m or j == n:#当数量小于0或者超出边界则不合法
                return False
            c = c + 1 if grid[i][j] == "(" else c - 1#更新数量
            return c == 0 if i == m - 1 and j == n - 1 else dfs(i + 1, j, c) or dfs(i, j + 1, c) #如果已经到了右下角则根据数量返回是否合法,否则继续深搜
        m, n = len(grid), len(grid[0])
        return dfs(0, 0, 0)

作者:qin-qi-shu-hua-2
链接:https://leetcode-cn.com/problems/check-if-there-is-a-valid-parentheses-string-path/solution/python-ji-yi-hua-shen-sou-by-qin-qi-shu-5yhnr/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

301 删除无效括号

力扣

没通过所有case的解法

#先统计出左右括号的数量,列举出需要删除的左括号和右括号,然后枚举需要删除的左括号和右括号
#注意回溯的时候要保证括号有效
# leetcode submit region begin(Prohibit modification and deletion)
class Solution:
    def removeInvalidParentheses(self, s: str) -> List[str]:
        def cal_delete(s):
            left_count = 0
            right_count = 0
            for i in range(len(s)):
                if s[i] == "(":
                    left_count += 1
                elif s[i] == ")":
                    right_count += 1
            return left_count, right_count

        def isvalid(s):
            left = 0
            for i in range(len(s)):
                if s[i] == "(":
                    left += 1
                elif s[i] == ")":
                    left -= 1
                if left < 0:
                    return False
            return left == 0

        def cal_path(path):
            cur_res = []
            for i in range(len(s)):
                if i not in path:
                    cur_res.append(s[i])
            return "".join(cur_res)

            # flag为1表示需要删除左括号,为0表示需要删除右括号

        def dfs(index, need_del_count, flag):
            if need_del_count == 0:
                cur_res = cal_path(path)
                if isvalid(cur_res) and cur_res not in res:
                    res.append(cur_res)
            for i in range(index, len(s)):
                if flag == 1:
                    if s[i] == "(" and need_del_count > 0:
                        path.append(i)
                        dfs(i + 1, need_del_count - 1, flag)
                        path.pop()
                    else:
                        dfs(i + 1, need_del_count, flag)
                if flag == 0:
                    if s[i] == ")" and need_del_count > 0:
                        path.append(i)
                        dfs(i + 1, need_del_count - 1, flag)
                        path.pop()
                    else:
                        dfs(i + 1, need_del_count, flag)

        res = []
        path = []
        left_count, right_count = cal_delete(s)
        if left_count > right_count:
            need_del_count = left_count - right_count
            flag = 1
        else:
            need_del_count = right_count - left_count
            flag = 0
        dfs(0, need_del_count, flag)
        if len(res) == 0:
            return [""]
        return res

回溯+括号判断

class Solution(object):
    def removeInvalidParentheses(self, s):
        """
        :type s: str
        :rtype: List[str]
        """
         # 统计需要删除的左括号数量和右括号数量,
        # 注意不能统计左右括号数量然后两者相减,因为比如")("这种情况。一定要考虑先后顺序
        def del_count(s):
            left_remove = 0
            right_remove = 0
            for i in range(len(s)):
                if s[i] == "(":
                    left_remove += 1
                elif s[i] == ")":
                    if left_remove > 0:
                        left_remove -= 1
                    else:
                        right_remove += 1
            return left_remove, right_remove

        def is_valid(s):
            left = 0
            for i in range(len(s)):
                if s[i] == "(":
                    left += 1
                elif s[i] == ")":
                    left -= 1
                if left < 0:
                    return False
            return left == 0

        left_remove, right_remove = del_count(s)
        path = []
        res = []

        def dfs(index, left_remove, right_remove, diff):
            if diff < 0:
                return
            if left_remove == 0 and right_remove == 0 and index == len(s):
                cur_res = "".join(path[:])
                if is_valid(cur_res) and cur_res not in res:
                    res.append(cur_res)
                return
            if index == len(s):
                return 
            if s[index] == "(":
                # 要当前的左括号
                path.append(s[index])
                dfs(index + 1, left_remove, right_remove, diff + 1)
                path.pop()
                # 不要当前的左括号
                if left_remove > 0:
                    dfs(index + 1, left_remove - 1, right_remove, diff)
            elif s[index] == ")":
                # 要当前的右括号
                path.append(s[index])
                dfs(index + 1, left_remove, right_remove, diff - 1)
                path.pop()
                # 不要当前的右括号
                if right_remove > 0:
                    dfs(index + 1, left_remove, right_remove - 1, diff)
            else:
                path.append(s[index])
                dfs(index + 1, left_remove, right_remove, diff)
                path.pop()
        dfs(0, left_remove, right_remove, 0)
        return res

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值