Leetcode 1106:解析布尔表达式(超详细!!!)

给你一个以字符串形式表述的布尔表达式(boolean) expression,返回该式的运算结果。

有效的表达式需遵循以下约定:

  • "t",运算结果为True
  • "f",运算结果为False
  • "!(expr)",运算过程为对内部表达式expr进行逻辑非的运算(NOT)
  • "&(expr1,expr2,...)",运算过程为对 2 个或以上内部表达式 expr1, expr2, ...进行逻辑与的运算(AND)
  • "|(expr1,expr2,...)",运算过程为对 2 个或以上内部表达式 expr1, expr2, ... 进行逻辑或的运算(OR)

示例 1:

输入:expression = "!(f)"
输出:true

示例 2:

输入:expression = "|(f,t)"
输出:true

示例 3:

输入:expression = "&(t,f)"
输出:false

示例 4:

输入:expression = "|(&(t,f,t),!(t))"
输出:false

提示:

  • 1 <= expression.length <= 20000
  • expression[i] 由 {'(', ')', '&', '|', '!', 't', 'f', ','} 中的字符组成。
  • expression是以上述形式给出的有效表达式,表示一个布尔值。

解题思路

这个问题使用python的话,只需要一行搞定(通过eval

class Solution:
    def parseBoolExpr(self, S: str, t=True, f=False) -> bool:
        return eval(S.replace('!', 'not |').replace('&(', 'all([').replace('|(', 'any([').replace(')', '])')) 

哈哈QAQ~~

但是上面这个写法有点作弊了。实际上这个问题和[Leetcode 1096:花括号展开 II(超详细!!!)](https://blog.csdn.net/qq_17550379/article/details/93711210)有点类似。所以我们可以先对这个问题进行拆分。

我们的问题需要处理三种运算符,并且这三种运算符的优先级是一样的。我们定义函数 f f f处理整个问题,定义函数 f 1 f_1 f1处理'&',定义函数 f 2 f_2 f2处理'|',定义函数 f 3 f_3 f3处理'!',那么

  • f ( x ) = f 1 ( x ) , f 2 ( x ) , f 3 ( x ) f(x)={f_1(x),f_2(x),f_3(x)} f(x)=f1(x),f2(x),f3(x)
  • KaTeX parse error: Expected 'EOF', got '&' at position 8: f_1(x)=&̲f(x)
  • f 2 ( x ) = ∣ f ( x ) f_2(x)=|f(x) f2(x)=f(x)
  • f 3 ( x ) = ! f ( x ) f_3(x)=!f(x) f3(x)=!f(x)

那么代码就很好写了

import operator
class Solution:
    def parseBoolExpr(self, s: str) -> bool:
        u = 0
            
        def f1(op):
            nonlocal u
            u += 2
            res = op(True if op == operator.__and__ else False, f())
            while s[u] != ')':
                u += 1
                res = op(res, f())
            u += 1
            return res

        def f2():
            nonlocal u
            u += 2
            res = not f()
            u += 1
            return res
        
        def f():
            nonlocal u
            if s[u] == 't':
                u += 1
                return True
            elif s[u] == 'f':
                u += 1
                return False
            elif s[u] == '&':
                return f1(operator.__and__)
            elif s[u] == '|':
                return f1(operator.__or__)
            else:
                return f2()
        
        return f()

我们也可以对每个"()"对分层考虑,对于每一层我们先处理笛卡尔积然后处理并集操作。我们可以通过变量level记录括号的层级信息,当我们碰到"("的时候,我们的level++;当我们碰到")",我们的level--,当level==0的时候就表示我们此时的一层遍历结束,我们需要递归处理这一层里面的表达式。例如

| ( & ( t , f , t ) , ! ( t ) )
      l           r

此时exp[l:r]就是属于同一层级的表达式,我们需要递归处理exp[l:r]里面的内容。

level==0并且遍历到的元素是'&', '|', '!'的话,我们只需要记录相应的操作符即可;如果level==0,但是遍历到的元素是字母的话,我们只需将字母对应的bool值添加到groups中。

import operator, functools
class Solution:
    def parseBoolExpr(self, expression: str, op=None) -> bool:
        groups = []
        level = 0
        
        pre = op
        for i, c in enumerate(expression):
            if c == '(':
                if level == 0:
                    start = i + 1
                level += 1
            elif c == ')':
                level -= 1
                if level == 0:
                    groups.append(self.parseBoolExpr(expression[start:i], op))
                    op = pre
            elif c == ',':
                continue
            elif level == 0:
                if c == '&':
                    op = operator.__and__
                elif c == '|':
                    op = operator.__or__
                elif c == '!':
                    op = operator.__not__
                elif c == 't':
                    groups.append(True)
                else:
                    groups.append(False)
        
        if op == None and groups:
            return groups[0]
        if op == operator.__not__:
            return not groups[0]
        return functools.reduce(op, groups)

上面这个代码在写法上有许多陷阱,首先你不能使用reduce(operator.__not__, [False])这种操作,因为当reduce的操作参数只有一个的时候,它会直接返回它(也就是这里直接返回False),所以我们需要单独考虑operator.__not__。另外我们需要记录op最初的状态,因为在递归的过程中op会改变(可以自己调试代码体会)。

reference:

https://leetcode.com/problems/parsing-a-boolean-expression/discuss/323307/Python-Easy-1-line-Cheat

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

你可能感兴趣的:(Problems,leetcode解题指南)