1106. Parsing A Boolean Expression
Return the result of evaluating a given boolean expression
, represented as a string.
An expression can either be:
"t"
, evaluating toTrue
;"f"
, evaluating toFalse
;"!(expr)"
, evaluating to the logical NOT of the inner expressionexpr
;"&(expr1,expr2,...)"
, evaluating to the logical AND of 2 or more inner expressionsexpr1, expr2, ...
;"|(expr1,expr2,...)"
, evaluating to the logical OR of 2 or more inner expressionsexpr1, expr2, ...
Example 1:
Input: expression = "!(f)"
Output: true
Example 2:
Input: expression = "|(f,t)"
Output: true
Example 3:
Input: expression = "&(t,f)"
Output: false
Example 4:
Input: expression = "|(&(t,f,t),!(t))"
Output: false
Constraints:
1 <= expression.length <= 20000
expression[i]
consists of characters in{'(', ')', '&', '|', '!', 't', 'f', ','}
.expression
is a valid expression representing a boolean, as given in the description.
Solution 1
class Solution:
def parseBoolExpr(self, expression: str) -> bool:
# lets try parsing it as a prefix notated expression
stack = []
count = 0
operators = {"|", "&", "!"}
for tok in expression:
if tok == "f":
stack.append(False)
elif tok == "t":
stack.append(True)
elif tok == "(":
stack.append(tok)
elif tok in operators:
stack.append(tok)
elif tok == ")":
# here is where we have to do the op stuff
# popoff until we get to the beginning of expression
operands = []
temp = -1
while stack and temp != "(":
temp = stack.pop()
if temp not in {"(", ","}:
operands.append(temp)
# now do the op
op = stack.pop()
if op == "&":
stack.append(all(operands))
elif op == "|":
stack.append(any(operands))
elif op == "!":
stack.append(not operands[0])
return stack.pop()
This solution is really long, so it can be hard to understand at first. In essence we’re parsing this boolean expression as if it were in Polish/Prefix notation. We iterate through the list once, using a stack to keep track of what we have already seen. Our algorithm goes something like this:
- For each token in the expression:
- If token is a 't' or 'f' value
- Add True/False to the stack
- Else if the token is an operator or the "(" symbol
- Add the token to the stack
- Else if our token is ")"
- Here, we're at the end of an expression.
- We go backwards until we hit the matching "(" token
- Now, we have all of our operands in a list.
- We evaluate the operands and then append the result to our stack
In the end, our stack should be left with only one value (if we did everything right). We pop off that value and return it.
Solution 2: Refactored with Lambda
class Solution:
def parseBoolExpr(self, expression: str) -> bool:
# lets try parsing it as a prefix notated expression
stack = []
operators = {
"|": any,
"&": all,
"!": lambda nums: not nums[0]
}
for tok in expression:
if tok == "f":
stack.append(False)
elif tok == "t":
stack.append(True)
elif tok in operators or tok == "(":
stack.append(tok)
elif tok == ")":
# here is where we have to do the op stuff
# popoff until we get to the beginning of expression
operands = []
temp = -1
while stack and temp != "(":
temp = stack.pop()
if temp not in {"(", ","}:
operands.append(temp)
# now do the op
stack.append(operators[stack.pop()](operands))
return stack.pop()
This solution is functionally the same as the last one, but it is refactored to use Python’s lambda expressions. This maps each operator to the function that we want to use when we evaluate it.
This solution works the exact same way, but we are able to make it much shorter by using lambda functions.
Notes
This one didn’t really feel like a Leetcode hard problem. Or maybe I’m just saying that because I actually solved it.