「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战」。
给定一个布尔表达式和一个期望的布尔结果 result,布尔表达式由 0 (false)、1 (true)、& (AND)、 | (OR) 和 ^ (XOR) 符号组成。实现一个函数,算出有几种可使该表达式得出 result 值的括号方法。
一、分析
布尔结果result,不是0就是1。表达式为奇数个(偶数位上为0|1,奇数位上为逻辑运算符(& | ^))
定义函数f(str, result),返回方法数
L...R上,一定有奇数个字符
L位置的字符和R位置上的字符,非0即1,不能是逻辑运算符号!
返回str[L...R]这一段,为true的方法数和为false的方法数
比如:0~4 & 6~10
如果result为true,那么就得要求0~4
返回true,6~10
返回true,result才为true
如果result为false,那么0~4
返回true,6~10
必为false,result才为false。或者0~4
返回false,6~10
返回true或false
二、实现
public static int countEval(String express, int desired) {
if (express == null || express.equals("")) {
return 0;
}
char[] exp = express.toCharArray();
int N = exp.length;
Info[][] dp = new Info[N][N];
Info allInfo = func(exp, 0, exp.length - 1, dp);
return desired == 1 ? allInfo.t : allInfo.f;
}
public static class Info {
public int t;
public int f;
public Info(int tr, int fa) {
t = tr;
f = fa;
}
}
// 限制:
// L...R上,一定有奇数个字符
// L位置的字符和R位置的字符,非0即1,不能是逻辑符号!
// 返回str[L...R]这一段,为true的方法数,和false的方法数
public static Info func(char[] str, int L, int R, Info[][] dp) {
if (dp[L][R] != null) {
return dp[L][R];
}
int t = 0;
int f = 0;
if (L == R) { // base case
t = str[L] == '1' ? 1 : 0;
f = str[L] == '0' ? 1 : 0;
} else { // L..R >=3
// 每一个种逻辑符号,split枚举的东西
// 都去试试最后结合
for (int split = L + 1; split < R; split += 2) {
Info leftInfo = func(str, L, split - 1, dp);
Info rightInfo = func(str, split + 1, R, dp);
int a = leftInfo.t;
int b = leftInfo.f;
int c = rightInfo.t;
int d = rightInfo.f;
switch (str[split]) {
case '&':
t += a * c;
f += b * c + b * d + a * d;
break;
case '|':
t += a * c + a * d + b * c;
f += b * d;
break;
case '^':
t += a * d + b * c;
f += a * c + b * d;
break;
}
}
}
dp[L][R] = new Info(t, f);
return dp[L][R];
}
三、总结
int f(str,F/T)
范围尝试模型
不是0就是1的位置为X
逻辑运算符的位置为Y
怎么划分可能性:以奇数位置划分逻辑运算符的情况下