Parsing A Boolean Expression
Stack characters until `)`; pop until `(`, evaluate the operator above, and push the result.
4 min read
stack parsing
Problem#
Evaluate a boolean expression containing t (true), f (false), !(expr), &(expr1,expr2,...), and |(expr1,expr2,...). Return true or false.
Examples#
"&(|(f))"→false"|(f,f,f,t)"→true"!(&(f,t))"→true
Constraints#
1 <= expression.length <= 2 * 10^4. The expression is always valid.
Approach#
Push every character except ,. On ), pop operands until (, pop the (, pop the operator above, evaluate, push the resulting t/f.
Solution#
class Solution {public: bool parseBoolExpr(string expression) { stack<char> st; for (char c : expression) { if (c == ',') continue; if (c != ')') { st.push(c); continue; } int t = 0, f = 0; while (st.top() != '(') { char x = st.top(); st.pop(); if (x == 't') ++t; else ++f; } st.pop(); // '(' char op = st.top(); st.pop(); bool val; if (op == '!') val = (t == 0); else if (op == '&') val = (f == 0); else val = (t > 0); st.push(val ? 't' : 'f'); } return st.top() == 't'; }};func parseBoolExpr(expression string) bool { st := []byte{} for i := 0; i < len(expression); i++ { c := expression[i] if c == ',' { continue } if c != ')' { st = append(st, c) continue } t, f := 0, 0 for st[len(st)-1] != '(' { x := st[len(st)-1] st = st[:len(st)-1] if x == 't' { t++ } else { f++ } } st = st[:len(st)-1] // '(' op := st[len(st)-1] st = st[:len(st)-1] var val bool switch op { case '!': val = t == 0 case '&': val = f == 0 default: val = t > 0 } if val { st = append(st, 't') } else { st = append(st, 'f') } } return st[len(st)-1] == 't'}class Solution: def parseBoolExpr(self, expression: str) -> bool: st: List[str] = [] for c in expression: if c == ',': continue if c != ')': st.append(c) continue t = f = 0 while st[-1] != '(': x = st.pop() if x == 't': t += 1 else: f += 1 st.pop() # '(' op = st.pop() if op == '!': val = t == 0 elif op == '&': val = f == 0 else: val = t > 0 st.append('t' if val else 'f') return st[-1] == 't'function parseBoolExpr(expression) { const st = []; for (const c of expression) { if (c === ',') continue; if (c !== ')') { st.push(c); continue; } let t = 0, f = 0; while (st[st.length - 1] !== '(') { const x = st.pop(); if (x === 't') t++; else f++; } st.pop(); // '(' const op = st.pop(); let val; if (op === '!') val = t === 0; else if (op === '&') val = f === 0; else val = t > 0; st.push(val ? 't' : 'f'); } return st[st.length - 1] === 't';}class Solution { public boolean parseBoolExpr(String expression) { Deque<Character> st = new ArrayDeque<>(); for (int i = 0; i < expression.length(); i++) { char c = expression.charAt(i); if (c == ',') continue; if (c != ')') { st.push(c); continue; } int t = 0, f = 0; while (st.peek() != '(') { char x = st.pop(); if (x == 't') t++; else f++; } st.pop(); // '(' char op = st.pop(); boolean val; if (op == '!') val = t == 0; else if (op == '&') val = f == 0; else val = t > 0; st.push(val ? 't' : 'f'); } return st.peek() == 't'; }}function parseBoolExpr(expression: string): boolean { const st: string[] = []; for (const c of expression) { if (c === ',') continue; if (c !== ')') { st.push(c); continue; } let t = 0, f = 0; while (st[st.length - 1] !== '(') { const x = st.pop(); if (x === 't') t++; else f++; } st.pop(); // '(' const op = st.pop(); let val: boolean; if (op === '!') val = t === 0; else if (op === '&') val = f === 0; else val = t > 0; st.push(val ? 't' : 'f'); } return st[st.length - 1] === 't';}Editorial#
For each closing ), the matching ( is one frame down. Between them are the operands. ! always has one operand, so checking t == 0 (i.e., its only operand was f) covers it. & is false iff any operand is false; | is true iff any operand is true.
Complexity#
- Time: O(n).
- Space: O(n).
Concept revision#
Related#