题目
给定一个布尔表达式和一个期望的布尔结果 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 个
解题思路
- 将表达式能够产生值的部分保存在一个数组dp中。
- 遍历表达式中的符号,能够将左右两部分分成两个子表达式。
- 求出左右两个子表达式中可能出现0,1的次数,之后再求出总表达式的为0,1的值的个数。
- dp数组用-1初始化,这样在求解过程中就能够知道该区间是否被计算过。
代码
class Solution {
public:
int countEval(string s, int result) {
//直接用dfs复杂度太高,划分子问题:确定一个中间符号mid,求出左右运算的0和1的个数
//然后就能求出【0,mid-1】和【mid + 1,right】的0和1的个数,最终扩展到【0,n】
//先将数字和符号分开存放
int n = s.size();
int m = n / 2 + 1;
vector<vector<vector<int>>> dp(m, vector<vector<int>>(m, vector<int>(2, -1)));
//枚举区间【i,j】内的结果
DFS(s, 0, n - 1, dp);
return dp[0][m - 1][result];
}
private:
void DFS(string &s, int start, int end, vector<vector<vector<int>>> &dp){
if(start == end){
if(dp[start / 2][start / 2][0] != -1){
return;
}
dp[start / 2][start / 2][0] = 0;
dp[start / 2][start / 2][1] = 0;
dp[start / 2][start / 2][s[start] - '0']++;
// cout << "i = " << start / 2 << ", = " << dp[start / 2][start / 2][1];
return;
}
if(dp[start / 2][end / 2][0] != -1){
return;
}
dp[start / 2][end / 2][0] = 0;
dp[start / 2][end / 2][1] = 0;
//然后划分子问题,列举在【i,j】区间内出现符号的位置
for(int k = start + 1; k < end; k += 2){
//如果这个值已经算过,就不用再递归计算
if(dp[start / 2][(k - 1) / 2][0] == -1){
//需要递归计算
DFS(s, start, k - 1, dp);
}
if(dp[(k + 1) / 2][end / 2][0] == -1){
//需要递归计算
DFS(s, k + 1, end, dp);
}
int s0 = dp[start / 2][(k - 1) / 2][0];
int s1 = dp[start / 2][(k - 1) / 2][1];
int e0 = dp[(k + 1) / 2][end / 2][0];
int e1 = dp[(k + 1) / 2][end / 2][1];
//然后计算当前的值,即左区间的0、1的个数已知,右区间0、1的个数已知。
if(s[k] == '&'){
dp[start / 2][end / 2][1] += s1 * e1;
dp[start / 2][end / 2][0] += s0 * e0 + s0 * e1 + s1 * e0;
}
else if(s[k] == '|'){
dp[start / 2][end / 2][1] += s0 * e1 + s1 * e1 + s1 * e0;
dp[start / 2][end / 2][0] += s0 * e0;
}
else{
dp[start / 2][end / 2][1] += s1 * e0 + e1 * s0;
dp[start / 2][end / 2][0] += s0 * e0 + s1 * e1;
}
}
return;
}
};