题目描述
描述:给定一个布尔表达式和一个期望的布尔结果 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 个
解题思路
思路1:最直观的想法是,区间dp。使用dp[i][j][0]表示区间i~j计算结果为0的方案数,使用dp[i][j][1]表示区间i~j计算结果为1的方案数,然后分情况讨论。首先按照区间从小到大来遍历,假设区间长度为step+1(此处为了方便处理下标),其中i表示区间起始位置,j表示区间的运算符,以j为分割,将区间分为[i,j-1]与[j+1,i+step]。假如j为&,那么结果为0的情况有0/0、0/1和1/0,结果为1的情况有1/1;假如j为|,那么结果为0的情况有0/0,结果为1的情况有1/1、0/1和1/0;假如j为^,那么结果为0的情况有0/0和1/1,结果为1的情况有0/1和1/0。注意:由于表达式是数字和运算符间隔的,故i、j、step均是+=2;由于(i,i+step)可以分为多种区间情况,故递推公式为+=。
int countEval(string s, int result)
{
int n=s.size();
//dp[i][j][0]表示i~j结果为0的方案数 dp[i][j][1]表示i~j结果为1的方案数
vector<vector<vector<int>>> dp(n,vector<vector<int>>(n,vector<int>(2,0)));
//初始化dp[i][i][0]和dp[i][i][1]
for(int i=0;i<n;i+=2)
{
dp[i][i][0]=s[i]=='0'?1:0;
dp[i][i][1]=s[i]=='1'?1:0;
}
//step表示表达式长度减去1 1^0|0|1
//1 1^0 1^0|0 1^0|0|1
//从小部分长度到大部分长度
for(int step=0;step<n;step+=2)
{
//i表示表达式起始位置
for(int i=0;i+step<n;i+=2)
{
//j表示表达式中的运算符 以j作为分割
for(int j=i+1;j<i+step;j+=2)
{
//将step分为[i,j-1]以及[j+1,i+step]
int left0=dp[i][j-1][0],left1=dp[i][j-1][1];
int right0=dp[j+1][i+step][0],right1=dp[j+1][i+step][1];
//第一种j=&
if(s[j]=='&')
{
//0: 0 0 ; 0 1 ; 1 0
dp[i][i+step][0]+=left0*right0+left0*right1+left1*right0;
//1: 1 1
dp[i][i+step][1]+=left1*right1;
}
//第二种j=|
else if(s[j]=='|')
{
//0: 0 0
dp[i][i+step][0]+=left0*right0;
//1: 1 1 ; 0 1 ; 1 0
dp[i][i+step][1]+=left1*right1+left0*right1+left1*right0;
}
//第三种j=^
else if(s[j]=='^')
{
//0: 0 0 ; 1 1
dp[i][i+step][0]+=left0*right0+left1*right1;
//1: 0 1 ; 1 0
dp[i][i+step][1]+=left0*right1+left1*right0;
}
}
}
}
return dp[0][n-1][result];
}
注意:按照区间从小到大来依次计算!!