Skip to main content
Back to problems
#1896
Hard Algorithms

Minimum cost to change the final value of expression

Math String Dynamic Programming Stack
51.2% acceptance
Feb 25, 2026
248
43
Given a valid boolean expression as a string, return the minimum cost to change the final value. Operations: flip 0↔1 (cost 1) or flip &↔| (cost 1). No operator precedence except parentheses; evaluate left-to-right.

Solution

Rust
Time O(n)
Space O(n)
LeetCode
solution.rs
impl Solution {
  pub fn min_operations_to_flip(expression: String) -> i32 {
    // Stack of (value, cost_to_flip)
    // value: current value of sub-expression
    // cost: minimum cost to flip it
    let mut stack: Vec<(i32, i32)> = Vec::new();
    let mut op_stack: Vec<u8> = Vec::new(); // operators: b'&' or b'|'

    let combine = |a: (i32, i32), b: (i32, i32), op: u8| -> (i32, i32) {
      let (av, ac) = a;
      let (bv, bc) = b;
      if op == b'&' {
        let val = av & bv;
        let cost = if val == 1 {
          // To flip to 0: flip either operand (min cost)
          ac.min(bc)
        } else {
          // To flip to 1: if both 0, flip both or change op
          if av == 1 || bv == 1 {
            // one is 0, one is 1: flip the 0, or change & to |
            if av == 0 { ac.min(1) } else { bc.min(1) }
          } else {
            // both are 0: flip one of them AND change op, or flip both
            // change & to |: cost 1 + flip whichever side we want (but | of two 0s is still 0, so also need to flip one side) => actually if we change & to |, val becomes 0|0=0, still need to flip. 
            // Easiest: flip one side (cost min(ac,bc)) and change op (cost 1)? No.
            // Actually: to get 1 from 0&0: options:
            // - change & to | (cost 1): gives 0|0=0, still 0. 
            // - flip one 0->1 (cost min(ac,bc)): gives 1&0=0, still 0. Need to flip both OR flip one and change op.
            // - flip both (cost ac+bc): gives 1&1=1
            // - flip one and change op (cost min(ac,bc)+1): gives 1|0=1 or 0|1=1
            (ac + bc).min(ac.min(bc) + 1)
          }
        };
        (val, cost)
      } else {
        // op == '|'
        let val = av | bv;
        let cost = if val == 0 {
          // To flip to 1: flip either
          ac.min(bc)
        } else {
          if av == 0 || bv == 0 {
            // one is 1, one is 0: change | to & costs 1, or flip the 1
            if av == 1 { ac.min(1) } else { bc.min(1) }
          } else {
            // both 1: to get 0, need to flip both or flip one and change op
            (ac + bc).min(ac.min(bc) + 1)
          }
        };
        (val, cost)
      }
    };

    for &b in expression.as_bytes() {
      match b {
        b'0' => stack.push((0, 1)),
        b'1' => stack.push((1, 1)),
        b'(' => op_stack.push(b'('),
        b'&' | b'|' => op_stack.push(b),
        b')' => {
          // Pop the '(' from op_stack — the top of stack is result inside parens
          op_stack.pop(); // remove '('
          // If there's a pending operator, combine
          if let Some(&op) = op_stack.last() {
            if op == b'&' || op == b'|' {
              op_stack.pop();
              let b_val = stack.pop().unwrap();
              let a_val = stack.pop().unwrap();
              stack.push(combine(a_val, b_val, op));
            }
          }
        }
        _ => {}
      }
      // After pushing a value (0 or 1), check if we should combine
      if b == b'0' || b == b'1' {
        if let Some(&op) = op_stack.last() {
          if op == b'&' || op == b'|' {
            op_stack.pop();
            let b_val = stack.pop().unwrap();
            let a_val = stack.pop().unwrap();
            stack.push(combine(a_val, b_val, op));
          }
        }
      }
    }

    stack.pop().unwrap().1
  }
}