给你一个以字符串形式表述的布尔表达式
(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处理'!'
,那么
那么代码就很好写了
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
如有问题,希望大家指出!!!