一、题目
给定一个布尔表达式和一个期望的布尔结果 result,布尔表达式由 0 (false)、1 (true)、& (AND)、 | (OR) 和 ^ (XOR) 符号组成。实现一个函数,算出有几种可使该表达式得出 result 值的括号方法。
示例 1:
输入: s = "1^0|0|1", result = 0
输出: 2
解释: 两种可能的括号方法是
1^(0|(0|1))
1^((0|0)|1)
示例 2:
输入: s = "0&0&0&1^1|0", result = 1
输出: 10
提示:
- 运算符的数量不超过 19 个
二、代码
class Solution {
// 通过一个类封装两个信息,作为递归的返回值
public class Info {
// 最终计算结果为true的逻辑符号结合的方法数
private int tCnt;
// 最终计算结果为false的逻辑符号结合的方法数
private int fCnt;
public Info(int t, int f) {
this.tCnt = t;
this.fCnt = f;
}
}
public int countEval(String s, int result) {
// 过滤无效参数
if (s == null || s.length() == 0) {
return 0;
}
char[] str = s.toCharArray();
int n = str.length;
// 引入Info类型的dp缓存
Info[][] dp = new Info[n][n];
Info info = process(str, 0, str.length - 1, dp);
// 当前要求最终的结果为result,返回result结果的逻辑符号结合的方法数
return result == 1 ? info.tCnt : info.fCnt;
}
// 限制:
// L...R上,一定有奇数个字符
// L位置的字符和R位置的字符,非0即1,不能是逻辑符号!
// 返回str[L...R]这一段,为true的方法数,和false的方法数
public Info process(char[] str, int l, int r, Info[][] dp) {
// 先查看答案是否在缓存中,有的话就直接返回
if (dp[l][r] != null) {
return dp[l][r];
}
// 当前范围只有一个数
if (l == r) {
// 如果这个位置的数是1,那么true就有1个,false就有0个
int t = str[l] == '1' ? 1 : 0;
// 如果这个位置的数是0,那么true就有0个,false就有1个
int f = str[l] == '0' ? 1 : 0;
// 将结果存入dp
dp[l][r] = new Info(t, f);
return dp[l][r];
} else {
int t = 0;
int f = 0;
// 从范围上尝试所有的可能情况,i作为分割,将式子分成左右两个部分,然后分别去求不同左右两部分范围的结果,来累加出总的答案
// i指向的就是式子中的运算逻辑符号,这里就是把当前范围上所有的逻辑符号都作为分隔位置尝试一边,将所有符合条件的结果都累加起来
for (int i = l + 1; i < r; i += 2) {
// 向下递归获取左右两部分的结果
Info leftInfo = process(str, l, i - 1, dp);
Info rightInfo = process(str, i + 1, r, dp);
// 根据当前尝试的逻辑运算符号,来计算能让式子结果为true和false的方法数,并且累加到t和f中
// &:左右两边都是true,最终结果才能是true;左右两边至少存在一个false或者两边都是false,最终结果就是false
// |:左右两边都是至少存在一个true或者两边都是true,最终结果才能是true;左右两边至都是false,最终结果就是false
// ^:左右两边结果不相同,最终结果才能是true;左右两边结果相同,最终结果就是false
if (str[i] == '&') {
t += leftInfo.tCnt * rightInfo.tCnt;
f += leftInfo.tCnt * rightInfo.fCnt + leftInfo.fCnt * rightInfo.tCnt + leftInfo.fCnt * rightInfo.fCnt;
} else if (str[i] == '|') {
t += leftInfo.tCnt * rightInfo.fCnt + leftInfo.fCnt * rightInfo.tCnt + leftInfo.tCnt * rightInfo.tCnt;
f += leftInfo.fCnt * rightInfo.fCnt;
} else if (str[i] == '^') {
t += leftInfo.tCnt * rightInfo.fCnt + leftInfo.fCnt * rightInfo.tCnt;
f += leftInfo.fCnt * rightInfo.fCnt + leftInfo.tCnt * rightInfo.tCnt;
}
}
// 将结果存入dp
dp[l][r] = new Info(t, f);
return dp[l][r];
}
}
}
三、解题思路
左右两侧得到T、F的方法数,根据最后逻辑符号的不同来做组合.
可能会根据划分符号到底是&、|、^而不同。根据预期是True或者False可能表现形式也不同,但是左侧汇报True/ False的方法数,右侧汇报True/False的方法数,总能加工出来当前划分情况的结果。