Hard

题目描述

给你一个有效的布尔表达式字符串 expression,该字符串由字符 '1''0''&'(按位AND运算符)、'|'(按位OR运算符)、'('')' 组成。

  • 例如,"()1|1""(1)&()" 是无效的,而 "1""(((1))|(0))""1|(0&(1))" 是有效的表达式。

返回改变表达式最终值的最小开销。

  • 例如,如果 expression = "1|1|(0&0)&1",其值为 1|1|(0&0)&1 = 1|1|0&1 = 1|0&1 = 1&1 = 1。我们希望应用操作使新表达式求值为 0。

改变表达式最终值的开销是对表达式执行的操作次数。操作类型如下:

  • '1' 变为 '0'
  • '0' 变为 '1'
  • '&' 变为 '|'
  • '|' 变为 '&'

注意:在计算顺序中,'&' 不比 '|' 优先。首先计算括号,然后按从左到右的顺序。

示例 1:

输入:expression = "1&(0|1)"
输出:1
解释:我们可以通过使用 1 次操作将 '|' 改为 '&',将 "1&(0|1)" 变为 "1&(0&1)"。
新表达式求值为 0。

示例 2:

输入:expression = "(0&0)&(0&0&0)"
输出:3
解释:我们可以使用 3 次操作将 "(0&0)&(0&0&0)" 变为 "(0|1)|(0&0&0)"。
新表达式求值为 1。

示例 3:

输入:expression = "(0|(1|0&1))"
输出:1
解释:我们可以使用 1 次操作将 "(0|(1|0&1))" 变为 "(0|(0|0&1))"。
新表达式求值为 0。

约束:

  • 1 <= expression.length <= 10^5
  • expression 只包含 '1''0''&''|''('')'
  • 所有括号都正确匹配
  • 没有空括号(即:"()" 不是 expression 的子字符串)

解题思路

这是一个动态规划问题,需要使用栈来处理表达式的计算和状态维护。

核心思路:

对于任何子表达式,我们需要维护两个信息:

  • 将当前值变为0的最小开销
  • 将当前值变为1的最小开销

我们使用栈来模拟表达式的计算过程,栈中存储 [当前值, 变为0的开销, 变为1的开销]

状态转移:

  1. 数字处理

    • 遇到 ‘0’:当前值=0,变为0开销=0,变为1开销=1
    • 遇到 ‘1’:当前值=1,变为0开销=1,变为1开销=0
  2. 运算符处理: 对于 ‘&’ 运算:

    • 结果为0的开销:min(左边变0, 右边变0)
    • 结果为1的开销:左边变1 + 右边变1
    • 如果原运算符是 ‘|’,需要+1(改变运算符的开销)

    对于 ‘|’ 运算:

    • 结果为0的开销:左边变0 + 右边变0
    • 结果为1的开销:min(左边变1, 右边变1)
    • 如果原运算符是 ‘&’,需要+1
  3. 括号处理

    • ‘(’:压入栈作为分隔符
    • ‘)’:弹出直到遇到 ‘(’,处理括号内的表达式

算法流程:

  1. 遍历表达式字符
  2. 数字直接压栈
  3. 运算符压栈
  4. 遇到 ‘)’ 时处理括号内的完整表达式
  5. 最终栈顶元素包含整个表达式的状态信息

最后返回将最终结果翻转所需的最小开销。

代码实现

class Solution {
public:
    int minOperationsToFlip(string expression) {
        stack<vector<int>> stk; // [value, cost_to_0, cost_to_1]
        
        for (char c : expression) {
            if (c == '0') {
                stk.push({0, 0, 1}); // value=0, cost to make 0=0, cost to make 1=1
            } else if (c == '1') {
                stk.push({1, 1, 0}); // value=1, cost to make 0=1, cost to make 1=0
            } else if (c == '&' || c == '|') {
                stk.push({c, 0, 0}); // store operator
            } else if (c == '(') {
                stk.push({-1, 0, 0}); // marker for left parenthesis
            } else if (c == ')') {
                // Process the expression inside parentheses
                vector<vector<int>> temp;
                while (stk.top()[0] != -1) {
                    temp.push_back(stk.top());
                    stk.pop();
                }
                stk.pop(); // remove the '(' marker
                
                // Reverse to get correct order
                reverse(temp.begin(), temp.end());
                
                // Evaluate the expression
                vector<int> result = temp[0];
                for (int i = 1; i < temp.size(); i += 2) {
                    char op = temp[i][0];
                    vector<int> right = temp[i + 1];
                    
                    int val = (op == '&') ? (result[0] & right[0]) : (result[0] | right[0]);
                    int cost0, cost1;
                    
                    if (op == '&') {
                        cost0 = min(result[1], right[1]);
                        cost1 = result[2] + right[2];
                    } else { // op == '|'
                        cost0 = result[1] + right[1];
                        cost1 = min(result[2], right[2]);
                    }
                    
                    // Consider flipping the operator
                    int alt_cost0, alt_cost1;
                    if (op == '&') {
                        alt_cost0 = result[1] + right[1];
                        alt_cost1 = min(result[2], right[2]);
                        cost0 = min(cost0, alt_cost0 + 1);
                        cost1 = min(cost1, alt_cost1 + 1);
                    } else {
                        alt_cost0 = min(result[1], right[1]);
                        alt_cost1 = result[2] + right[2];
                        cost0 = min(cost0, alt_cost0 + 1);
                        cost1 = min(cost1, alt_cost1 + 1);
                    }
                    
                    result = {val, cost0, cost1};
                }
                
                stk.push(result);
            }
        }
        
        // Process remaining expression
        vector<vector<int>> temp;
        while (!stk.empty()) {
            temp.push_back(stk.top());
            stk.pop();
        }
        reverse(temp.begin(), temp.end());
        
        vector<int> result = temp[0];
        for (int i = 1; i < temp.size(); i += 2) {
            char op = temp[i][0];
            vector<int> right = temp[i + 1];
            
            int val = (op == '&') ? (result[0] & right[0]) : (result[0] | right[0]);
            int cost0, cost1;
            
            if (op == '&') {
                cost0 = min(result[1], right[1]);
                cost1 = result[2] + right[2];
            } else {
                cost0 = result[1] + right[1];
                cost1 = min(result[2], right[2]);
            }
            
            int alt_cost0, alt_cost1;
            if (op == '&') {
                alt_cost0 = result[1] + right[1];
                alt_cost1 = min(result[2], right[2]);
                cost0 = min(cost0, alt_cost0 + 1);
                cost1 = min(cost1, alt_cost1 + 1);
            } else {
                alt_cost0 = min(result[1], right[1]);
                alt_cost1 = result[2] + right[2];
                cost0 = min(cost0, alt_cost0 + 1);
                cost1 = min(cost1, alt_cost1 + 1);
            }
            
            result = {val, cost0, cost1};
        }
        
        return result[0] == 1 ? result[1] : result[2];
    }
};
class Solution:
    def minOperationsToFlip(self, expression: str) -> int:
        stack = []
        
        for c in expression:
            if c == '0':
                stack.append([0, 0, 1])  # [value, cost_to_0, cost_to_1]
            elif c == '1':
                stack.append([1, 1, 0])
            elif c in '&|':
                stack.append([ord(c), 0, 0])  # store operator as ASCII
            elif c == '(':
                stack.append([-1, 0, 0])  # marker
            elif c == ')':
                temp = []
                while stack[-1][0] != -1:
                    temp.append(stack.pop())
                stack.pop()  # remove '(' marker
                
                temp.reverse()
                result = temp[0]
                
                for i in range(1, len(temp), 2):
                    op = chr(temp[i][0])
                    right = temp[i + 1]
                    
                    val = (result[0] & right[0]) if op == '&' else (result[0] | right[0])
                    
                    if op == '&':
                        cost0 = min(result[1], right[1])
                        cost1 = result[2] + right[2]
                        # Try flipping to '|'
                        alt_cost0 = result[1] + right[1]
                        alt_cost1 = min(result[2], right[2])
                        cost0 = min(cost0, alt_cost0 + 1)
                        cost1 = min(cost1, alt_cost1 + 1)
                    else:  # op == '|'
                        cost0 = result[1] + right[1]
                        cost1 = min(result[2], right[2])
                        # Try flipping to '&'
                        alt_cost0 = min(result[1], right[1])
                        alt_cost1 = result[2] + right[2]
                        cost0 = min(cost0, alt_cost0 + 1)
                        cost1 = min(cost1, alt_cost1 + 1)
                    
                    result = [val, cost0, cost1]
                
                stack.append(result)
        
        # Process remaining expression
        temp = stack[:]
        result = temp[0]
        
        for i in range(1, len(temp), 2):
            op = chr(temp[i][0])
            right = temp[i + 1]
            
            val = (result[0] & right[0]) if op == '&' else (result[0] | right[0])
            
            if op == '&':
                cost0 = min(result[1], right[1])
                cost1 = result[2] + right[2]
                alt_cost0 = result[1] + right[1]
                alt_cost1 = min(result[2], right[2])
                cost0 = min(cost0, alt_cost0 + 1)
                cost1 = min(cost1, alt_cost1 + 1)
            else:
                cost0 = result[1] + right[1]
                cost1 = min(result[2], right[2])
                alt_cost0 = min(result[1], right[1])
                alt_cost1 = result[2] + right[2]
                cost0 = min(cost0, alt_cost0 + 1)
                cost1 = min(cost1, alt_cost1 + 1)
            
            result = [val, cost0, cost1]
        
        return result[1] if result[0] == 1 else result[2]
public class Solution {
    public int MinOperationsToFlip(string expression) {
        Stack<int[]> stack = new Stack<int[]>();
        
        foreach (char c in expression) {
            if (c == '0') {
                stack.Push(new int[] {0, 0, 1});
            } else if (c == '1') {
                stack.Push(new int[] {1, 1, 0});
            } else if (c == '&' || c == '|') {
                stack.Push(new int[] {c, 0, 0});
            } else if (c == '(') {
                stack.Push(new int[] {-1, 0, 0});
            } else if (c == ')') {
                List<int[]> temp = new List<int[]>();
                while (stack.Peek()[0] != -1) {
                    temp.Add(stack.Pop());
                }
                stack.Pop(); // remove '(' marker
                
                temp.Reverse();
                int[] result = temp[0];
                
                for (int i = 1; i < temp.Count; i += 2) {
                    char op = (char)temp[i][0];
                    int[] right = temp[i + 1];
                    
                    int val = (op == '&') ? (result[0] & right[0]) : (result[0] | right[0]);
                    int cost0, cost1;
                    
                    if (op == '&') {
                        cost0 = Math.Min(result[1], right[1]);
                        cost1 = result[2] + right[2];
                        int altCost0 = result[1] + right[1];
                        int altCost1 = Math.Min(result[2], right[2]);
                        cost0 = Math.Min(cost0, altCost0 + 1);
                        cost1 = Math.Min(cost1, altCost1 + 1);
                    } else {
                        cost0 = result[1] + right[1];
                        cost1 = Math.Min(result[2], right[2]);
                        int altCost0 = Math.Min(result[1], right[1]);
                        int altCost1 = result[2] + right[2];
                        cost0 = Math.Min(cost0, altCost0 + 1);
                        cost1 = Math.Min(cost1, altCost1 + 1);
                    }
                    
                    result = new int[] {val, cost0, cost1};
                }
                
                stack.Push(result);
            }
        }
        
        List<int[]> finalTemp = new List<int[]>();
        while (stack.Count > 0) {
            finalTemp.Add(stack.Pop());
        }
        finalTemp.Reverse();
        
        int[] finalResult = finalTemp[0];
        for (int i = 1; i < finalTemp.Count; i += 2) {
            char op = (char)finalTemp[i][0];
            int[] right = finalTemp[i + 1];
            
            int val = (op == '&') ? (finalResult[0] & right[0]) : (finalResult[0] | right[0]);
            int cost0, cost1;
            
            if (op == '&') {
                cost0 = Math.Min(finalResult[1], right[1]);
                cost1 = finalResult[2] + right[2];
                int altCost0 = finalResult[1] + right[1];
                int altCost1 = Math.Min(finalResult[2], right[2]);
                cost0 = Math.Min(cost0, altCost0 + 1);
                cost1 = Math.Min(cost1, altCost1 + 1);
            } else {
                cost0 = finalResult[1] + right[1];
                cost1 = Math.Min(finalResult[2], right[2]);
                int altCost0 = Math.Min(finalResult[1], right[1]);
                int altCost1 = finalResult[2] + right[2];
                cost0 = Math.Min(cost0, altCost0 + 1);
                cost1 = Math.Min(cost1, altCost1 + 1);
            }
            
            finalResult = new int[] {val, cost0, cost1};
        }
        
        return finalResult[0] == 1 ? finalResult[1] : finalResult[2];
    }
}
var minOperationsToFlip = function(expression) {
    let i = 0;
    
    function parse() {
        let left = parseFactor();
        
        while (i < expression.length && (expression[i] === '&' || expression[i] === '|')) {
            let op = expression[i++];
            let right = parseFactor();
            
            let val = op === '&' ? left[0] & right[0] : left[0] | right[0];
            let cost;
            
            if (op === '&') {
                if (val === 1) {
                    cost = Math.min(left[1], right[1]);
                } else {
                    cost = Math.min(left[2] + right[2], Math.min(left[2], right[2]) + 1);
                }
            } else {
                if (val === 0) {
                    cost = Math.min(left[2], right[2]);
                } else {
                    cost = Math.min(left[1] + right[1], Math.min(left[1], right[1]) + 1);
                }
            }
            
            left = [val, val === 1 ? cost : (op === '&' ? left[1] + right[1] : Math.min(left[1], right[1]) + 1), 
                    val === 0 ? cost : (op === '|' ? left[2] + right[2] : Math.min(left[2], right[2]) + 1)];
        }
        
        return left;
    }
    
    function parseFactor() {
        if (expression[i] === '(') {
            i++;
            let result = parse();
            i++;
            return result;
        } else {
            let val = parseInt(expression[i++]);
            return [val, val === 1 ? 0 : 1, val === 0 ? 0 : 1];
        }
    }
    
    let result = parse();
    return result[result[0] === 1 ? 2 : 1];
};

复杂度分析

复杂度类型复杂度
时间复杂度O(n)
空间复杂度O(n)

解释:

  • 时间复杂度:O(n),其中 n 是表达式的长度。每个字符最多被处理常数次。
  • 空间复杂度:O(n),栈的最大深度与表达式中括号的嵌套深度和操作数数量成正比。