【LeetCode 1896】Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务

91 阅读1分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务活动详情

一、题目描述

给定一个二进制表达式,包含()、1、0、&、| 几种符号,每次可以反转修改1、0、&、|,变成0、1、|、&。求最少需要修改几次,才能改变整个表达式的值。

数据范围

字符串长度 l <= 10^5

二、思路分析

想了二十分钟,先考虑怎么单纯的求值,用一个栈就可以了,遇到符号就入栈,遇到数字就合并,遇到左括号就入栈,遇到右括号就和左括号合并(此时括号内只会有一个数字)。但是接下来就卡住了,要怎么做才能最小修改呢?

尝试去列举了八种最简单的情况,基本都是修改一次两次即可。

我们是否可以考虑DP呢?但是每个数字修改后,对后面其实是会有后效性的,怎么解决这个问题?

通过思考我们发现,同一层之间是不会有后效性的,因为没有括号带来的“延迟计算”(有括号时,我们会出现当前的结果不能计算,得存起来)。同层之间没有后效性的问题也就使得我们在同一层内可以用 DP 来计算,不同层之间用栈来存储,等到它们直接同一层了(遇到右括号时),再合并起来。

借鉴一下自己写剑指Offer 224 时的思路,两个栈,一个存当前层的状态,一个存符号。每一层只需要用一个坑位来表示它的状态,同层之间可以立即计算,不用考虑后效性的问题。

新的问题是,状态如何表示呢?当计算未结束的时候,我们只知道我们需要翻转,但并不知道要转到 1 还是 0 ,故需要将两种状态同时记录下来。用一对数字表示,一个表示变成 0 所需要的最小操作数,另一个表示变成 1 所需要的最小操作数,两个状态之间的合并是很显然的。

三、AC代码

class Solution {
public:
    struct node{
        int x,y;
    };

    stack<node> num;
    stack<char> op;

    node merge(node aa, node bb, int flag){
        // flag==1 is "&" , flag==2 is "|"
        node cc;
        if (flag == 1) {
            cc.x = min(min(aa.x, bb.x), 1 + aa.x + bb.x);
            cc.y = min(aa.y + bb.y, 1 + min(aa.y, bb.y));
        } else {
            cc.x = min(aa.x + bb.x, 1 + min(aa.x, bb.x));
            cc.y = min(min(aa.y, bb.y), 1 + aa.y + bb.y);
        }
        return cc;
    }

    void cacl(){
        if (op.empty()) return;
        char ch = op.top();
        if (ch == '(') return;
        op.pop();
        node b = num.top();num.pop();
        node a = num.top();num.pop();
        
        node c = merge(a, b, ch == '&' ? 1 : 2);
        num.push(c);
        return;
    }


    int minOperationsToFlip(string expression) {
        string s = expression;
        int l = s.length();
        for (int i=0; i<l; i++) {
            if (s[i] == '(') {
                op.push(s[i]);
            } else
            if (s[i] == ')') {
                op.pop();
                cacl();
            } else
            if (s[i] == '|' || s[i] == '&') {
                op.push(s[i]);
            } else {
                int now = s[i] - '0';
                node cc;
                cc.x = now;
                cc.y = now^1;
                num.push(cc);
                cacl();
            }
        }
        node ans = num.top();
        if (ans.x == 0) return ans.y;
                    else return ans.x;
    }
};

四、总结

在写代码时遇到的一些坑: 表达式问题一定要非常注意一些边界问题(栈问题也是),最重要的是每次合并时要判断能否合并,如果迎接你的也是一个左括号(这一层没有数字),这就不能合并,要判断;每层的第一个数字也不能执行合并操作。

对于本题,最重要的一个点是,认识到每一层的层内是没有后效性的,这决定了我们可以使用 DP