来源第 54 场双周赛 Q4难度困难分数2531
题目描述
给你一个 有效的 布尔表达式,用字符串 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\)、 \(\&\)、 \(|\) 与括号组成,一次操作可把数字或运算符改成另一种,求改变整个表达式值的最少次数。表达式长度达 \(10^5\),不能对每种改法重新求值。
每个子表达式只需知道当前值,以及把它变成相反值的代价。叶子 \(0/1\) 翻转为 \(1\);两个子式用 \(\&\) 或 \(|\) 连接时,可根据左右值与运算符分类讨论:改运算符、只改一侧或两侧。用栈按括号与优先级解析,自底向上合并即可。
每个子表达式记为二元组 \((\textit{val},\textit{cost})\):当前布尔值,以及把它改成相反值的最少操作次数。数字 \(0/1\) 的翻转代价是 \(1\)。
\(\&\) 与 \(|\) 优先级相同,按从左到右计算,括号优先。两个栈分别保存子表达式和运算符。读到运算符时,先把栈里已有的同级运算归约;读到右括号时,归约到对应的左括号。
设左右子式为 \((v_1,c_1)\)、 \((v_2,c_2)\)。
- 运算符是 \(\&\),两端都是 \(1\):结果为 \(1\),把其中一端改成 \(0\),代价 \(\min(c_1,c_2)\)。
- 两端都是 \(0\):结果为 \(0\)。两端都改成 \(1\) 的代价是 \(c_1+c_2\);把 \(\&\) 改成 \(|\) 并至少改一端的代价是 \(1+\min(c_1,c_2)\)。
- 只有一端是 \(0\):结果为 \(0\)。把这个 \(0\) 改成 \(1\),或把 \(\&\) 改成 \(|\),取较小代价。
- 运算符是 \(|\) 时,三种情况与上面对偶。
归约结束后,栈顶的 \(\textit{cost}\) 就是答案。
时间复杂度 \(O(n)\),空间复杂度 \(O(n)\)。其中 \(n\) 是表达式的长度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 | class Solution:
def minOperationsToFlip(self, expression: str) -> int:
def merge(a, b, op):
v1, c1 = a
v2, c2 = b
if op == '&':
val = v1 & v2
if v1 == 1 and v2 == 1:
cost = min(c1, c2)
elif v1 == 0 and v2 == 0:
cost = min(c1 + c2, 1 + min(c1, c2))
else:
cost = min(c1 if v1 == 0 else c2, 1)
else:
val = v1 | v2
if v1 == 0 and v2 == 0:
cost = min(c1, c2)
elif v1 == 1 and v2 == 1:
cost = min(c1 + c2, 1 + min(c1, c2))
else:
cost = min(c1 if v1 == 1 else c2, 1)
return val, cost
nums = []
ops = []
def apply():
b = nums.pop()
a = nums.pop()
nums.append(merge(a, b, ops.pop()))
for c in expression:
if c == '(':
ops.append(c)
elif c in '01':
nums.append((int(c), 1))
elif c in '&|':
while ops and ops[-1] in '&|':
apply()
ops.append(c)
else:
while ops[-1] != '(':
apply()
ops.pop()
while ops:
apply()
return nums[-1][1]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 | class Solution {
public int minOperationsToFlip(String expression) {
int n = expression.length();
int[] val = new int[n];
int[] cost = new int[n];
int top = 0;
char[] ops = new char[n];
int otop = 0;
for (int i = 0; i < n; ++i) {
char c = expression.charAt(i);
if (c == '(') {
ops[otop++] = c;
} else if (c == '0' || c == '1') {
val[top] = c - '0';
cost[top++] = 1;
} else if (c == '&' || c == '|') {
while (otop > 0 && (ops[otop - 1] == '&' || ops[otop - 1] == '|')) {
merge(val, cost, top, ops[--otop]);
--top;
}
ops[otop++] = c;
} else {
while (ops[otop - 1] != '(') {
merge(val, cost, top, ops[--otop]);
--top;
}
--otop;
}
}
while (otop > 0) {
merge(val, cost, top, ops[--otop]);
--top;
}
return cost[0];
}
private void merge(int[] val, int[] cost, int top, char op) {
int v1 = val[top - 2], c1 = cost[top - 2];
int v2 = val[top - 1], c2 = cost[top - 1];
if (op == '&') {
val[top - 2] = v1 & v2;
if (v1 == 1 && v2 == 1) {
cost[top - 2] = Math.min(c1, c2);
} else if (v1 == 0 && v2 == 0) {
cost[top - 2] = Math.min(c1 + c2, 1 + Math.min(c1, c2));
} else {
cost[top - 2] = Math.min(v1 == 0 ? c1 : c2, 1);
}
} else {
val[top - 2] = v1 | v2;
if (v1 == 0 && v2 == 0) {
cost[top - 2] = Math.min(c1, c2);
} else if (v1 == 1 && v2 == 1) {
cost[top - 2] = Math.min(c1 + c2, 1 + Math.min(c1, c2));
} else {
cost[top - 2] = Math.min(v1 == 1 ? c1 : c2, 1);
}
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 | class Solution {
public:
int minOperationsToFlip(string expression) {
vector<pair<int, int>> nums;
vector<char> ops;
auto merge = [&](char op) {
auto b = nums.back();
nums.pop_back();
auto a = nums.back();
nums.pop_back();
int v1 = a.first, c1 = a.second, v2 = b.first, c2 = b.second;
int val, cost;
if (op == '&') {
val = v1 & v2;
if (v1 == 1 && v2 == 1) {
cost = min(c1, c2);
} else if (v1 == 0 && v2 == 0) {
cost = min(c1 + c2, 1 + min(c1, c2));
} else {
cost = min(v1 == 0 ? c1 : c2, 1);
}
} else {
val = v1 | v2;
if (v1 == 0 && v2 == 0) {
cost = min(c1, c2);
} else if (v1 == 1 && v2 == 1) {
cost = min(c1 + c2, 1 + min(c1, c2));
} else {
cost = min(v1 == 1 ? c1 : c2, 1);
}
}
nums.emplace_back(val, cost);
};
for (char c : expression) {
if (c == '(') {
ops.push_back(c);
} else if (c == '0' || c == '1') {
nums.emplace_back(c - '0', 1);
} else if (c == '&' || c == '|') {
while (!ops.empty() && (ops.back() == '&' || ops.back() == '|')) {
merge(ops.back());
ops.pop_back();
}
ops.push_back(c);
} else {
while (ops.back() != '(') {
merge(ops.back());
ops.pop_back();
}
ops.pop_back();
}
}
while (!ops.empty()) {
merge(ops.back());
ops.pop_back();
}
return nums[0].second;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 | func minOperationsToFlip(expression string) int {
type pair struct{ val, cost int }
nums := make([]pair, 0)
ops := make([]byte, 0)
merge := func(op byte) {
b := nums[len(nums)-1]
a := nums[len(nums)-2]
nums = nums[:len(nums)-2]
v1, c1, v2, c2 := a.val, a.cost, b.val, b.cost
val, cost := 0, 0
if op == '&' {
val = v1 & v2
if v1 == 1 && v2 == 1 {
cost = min(c1, c2)
} else if v1 == 0 && v2 == 0 {
cost = min(c1+c2, 1+min(c1, c2))
} else if v1 == 0 {
cost = min(c1, 1)
} else {
cost = min(c2, 1)
}
} else {
val = v1 | v2
if v1 == 0 && v2 == 0 {
cost = min(c1, c2)
} else if v1 == 1 && v2 == 1 {
cost = min(c1+c2, 1+min(c1, c2))
} else if v1 == 1 {
cost = min(c1, 1)
} else {
cost = min(c2, 1)
}
}
nums = append(nums, pair{val, cost})
}
for i := 0; i < len(expression); i++ {
c := expression[i]
if c == '(' {
ops = append(ops, c)
} else if c == '0' || c == '1' {
nums = append(nums, pair{int(c - '0'), 1})
} else if c == '&' || c == '|' {
for len(ops) > 0 && (ops[len(ops)-1] == '&' || ops[len(ops)-1] == '|') {
merge(ops[len(ops)-1])
ops = ops[:len(ops)-1]
}
ops = append(ops, c)
} else {
for ops[len(ops)-1] != '(' {
merge(ops[len(ops)-1])
ops = ops[:len(ops)-1]
}
ops = ops[:len(ops)-1]
}
}
for len(ops) > 0 {
merge(ops[len(ops)-1])
ops = ops[:len(ops)-1]
}
return nums[0].cost
}
|