xxleyi / loop_invariants

记录以及整理「循环不变式」视角下的算法题解

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

leetcode 1106. 解析布尔表达式

xxleyi opened this issue · comments

给你一个以字符串形式表述的 布尔表达式(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 是以上述形式给出的有效表达式,表示一个布尔值。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/parsing-a-boolean-expression
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


解:
此题是对前缀表达式进行求值。我们只需要依次入栈,并借助括号判断合适的子表达式求值时机。

求值子表达时,区分好运算符和运算数即可。

这道题的循环不变式如何体现呢?核心是洞察到后入先出的求值顺序,从而使用两个栈解决问题。这是不变的根本,有了这个,剩下的也变得很容易,无非是按部就班,分情况讨论。

/**
 * @param {string} expression
 * @return {boolean}
 */
var parseBoolExpr = function(expression) {
  const stack = []
  // 三种运算符的实现
  const ops = {
    // 多参
    '&': (args) => args.every(e => e === 't'),
    // 多参
    '|': (args) => args.some(e => e === 't'),
    // 单参
    '!': (args) => args[0] === 'f',
  }

  // 最内层子表达式的求值
  // 一直出栈,直至遇到一个运算符为止
  // 并将求值结果压入老栈
  function evaluateInnerPart() {
    const args = []
    let e
    while (true) {
      e = stack.pop()
      if (e in ops) {
        stack.push(ops[e](args) ? 't' : 'f')
        return
      }
      args.push(e)
    }
  }

  // 单趟循环,分情况处理
  for (let e of expression) {
    switch (e) {
      // 遇到右括号,需要求值最内层子表达时
      case ')':
        evaluateInnerPart()
        break
      // 遇到逗号和左括号,直接跳过
      case ',':
      case '(':
        break
      // 其余为运算符或运算数,直接入栈
      default:
        stack.push(e)
    }
  }
  // 循环过后,栈中唯一的运算数为整个表达式的求值结果
  // 并将此结果转化为 JS 中的相应运算符
  return stack.pop() === 't'
};