JAVA程序设计:反转表达式值的最少操作次数(LeetCode:1896)

给你一个 有效的 布尔表达式,用字符串 expression 表示。这个字符串包含字符 '1','0','&'(按位 与 运算),'|'(按位 或 运算),'(' 和 ')' 。

比方说,"()1|1" 和 "(1)&()" 不是有效 布尔表达式。而 "1", "(((1))|(0))" 和 "1|(0&(1))" 是 有效 布尔表达式。
你的目标是将布尔表达式的 值 反转 (也就是将 0 变为 1 ,或者将 1 变为 0),请你返回达成目标需要的 最少操作 次数。

比方说,如果表达式 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&(0|1)" 变成 "1&(0&1)" ,执行的操作为将一个 '|' 变成一个 '&' ,执行了 1 次操作。
新表达式的值为 0 。
示例 2:

输入:expression = "(0&0)&(0&0&0)"
输出:3
解释:我们可以将 "(0&0)&(0&0&0)" 变成 "(0|1)|(0&0&0)" ,执行了 3 次操作。
新表达式的值为 1 。
示例 3:

输入:expression = "(0|(1|0&1))"
输出:1
解释:我们可以将 "(0|(1|0&1))" 变成 "(0|(0|0&1))" ,执行了 1 次操作。
新表达式的值为 0 。
 

提示:

1 <= expression.length <= 105
expression 只包含 '1','0','&','|','(' 和 ')'
所有括号都有与之匹配的对应括号。
不会有空的括号(也就是说 "()" 不是 expression 的子字符串)。

思路:本题参考了官方题解的方法,解法说明比较详细,因此这里不再赘述多余的说明,仅贴出这题的核心思想。

本质上是和表达式求值类似的解法,首先栈是必不可少的,此外,由于不止一种运算符,而且还有括号的存在。我们需要两个栈:操作数栈操作符栈

和表达式求值不同的是,我们不能单单的存储每一个操作数,而是考虑题目的需求,即0变成1(1变成0)的最小操作次数。为了能够维护这两个信息,我们考虑在操作数栈中存储一个二元组(x,y):表示当前运算结果变成0的最小操作次数是x,变成1的最小操作次数为y。

接下来便是状态转移了,此时就需要分情况讨论:

(1)当前数为0,则压入操作数栈,其二元组为(0,1)。即0变成0需要0次,变成1需要1次。

(2)当前数为1,则压入操作数栈,其二元组为(1,0)。即0变成0需要1次,变成1需要0次。

(3)当前为运算符‘&’、‘|’、‘(’时则压入操作符栈,等待之后的运算。

(4)当前为右括号时:表示需要进行运算,此时若操作数栈中元素个数为1的话则直接省略运算,否则提取出栈顶的两个二元组进行运算,为了保证运算结果为0或1的运算次数最小,我们的转移规则为(假设两个二元组为(x1,y1),(x2,y2)):

(1)当前运算符为‘&’时,此时只有两个1相与的结果才是1,否则一定是0,因此:

x3_and=min(x1+y1,x1+y2,x2+y1); y3_and=y1+y2

(2)当前运算符为‘|’时,此时只有两个0相或的结果才是0,否则一定是1,因此:

x3_or=x1+x2; y3_or=min(y1+x2,x1+y2,y1+y2)

此外,由于题目中存在修改运算符的操作,因此我们考虑与或运算之间的转化:

x3_and=min(x3_and,x3_or+1).  x3_or=min(x3_or,x3_and+1)

问题得到解决!

class Solution {
    public int minOperationsToFlip(String expression) {
        int len = expression.length();
        Deque<int[]> st_num = new ArrayDeque<>();
        Deque<Character> st_opt = new ArrayDeque<>();
        for (int i = 0; i < len; i++) {
            char c = expression.charAt(i);
            if (c == '(' || c == '&' || c == '|') {
                st_opt.add(c);
                continue;
            } else if (c == '0')
                st_num.add(new int[]{0, 1});
            else if (c == '1')
                st_num.add(new int[]{1, 0});
            else
                st_opt.pollLast();
            calc(st_num, st_opt);
        }
        return Math.max(st_num.peekLast()[0], st_num.peekLast()[1]);
    }

    private void calc(Deque<int[]> st1, Deque<Character> st2) {
        if (st1.size() >= 2 && (st2.peekLast() == '|' || st2.peekLast() == '&')) {
            int[] top1 = st1.pollLast();
            int[] top2 = st1.pollLast();
            int[] result_add = opt_and(top1, top2);
            int[] result_or = opt_or(top1, top2);
            if (st2.peekLast() == '&')
                st1.addLast(new int[]{Math.min(result_add[0], result_or[1] + 1), Math.min(result_add[1], result_or[1] + 1)});
            else
                st1.addLast(new int[]{Math.min(result_add[0] + 1, result_or[0]), Math.min(result_add[1] + 1, result_or[1])});
            st2.pollLast();
        }
    }

    private int[] opt_or(int[] a, int[] b) {
        int[] res = new int[2];
        res[0] = a[0] + b[0];
        res[1] = Math.min(a[0] + b[1], Math.min(a[1] + b[0], a[1] + b[1]));
        return res;
    }

    private int[] opt_and(int[] a, int[] b) {
        int[] res = new int[2];
        res[0] = Math.min(a[0] + b[0], Math.min(a[0] + b[1], a[1] + b[0]));
        res[1] = a[1] + b[1];
        return res;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值