表达式得到期望结果的组成种数
给定一个只由0(假)、1(真)、&(逻辑与)、|(逻辑或)和^(异或)五种字符组成的字符串express,再给定一个布尔值desired。返回express能有多少种组合方式,可以达到desired的结果。
express=“1^0|0|1”,desired=false。
只有1^((0|0)|1)和1^(0|(0|1))的组合可以得到false。返回2。
express=“1”,desired=false。
没有组合可以得到false。返回0。
思路 & 代码
暴力递归
先对表达式必须进行判断,必须满足一下几点
- 长度必须为偶数
- 奇数位上是0或者1
- 偶数位上是^|&
然后对于每一个运算符作为分界点,分别计算左右两边的方法数使得满足disired,然后相乘得到方法数,再把所有方法数相加即可
优化递归
使用数组记录算过的值
由于p(char[] exp, boolean desired, int l, int r)递归中有3个变量,应该使用三维表进行记录
但是使用2张二维表就可以,因为boolean desired只有真假两种取值
仍然是以位运算符号作为分割,计算所有的方法数,并记录
- t[i][j]表示第i到j([i,j])的exp中结果为true的结果数
- f[i][j]表示第i到j([i,j])的exp中结果为false的结果数12345678910111213141516171819202122232425262728293031323334353637383940414243444546//优化递归,记录所计算的值//p(char[] exp, boolean desired, int l, int r)递归中有3个变量,但是使用2张二维表就可以//一个表记录结果是true,另一个记录结果是falsepublic static int num2(String express, boolean desired) {if (express == null || express.equals("")) {return 0;}char[] exp = express.toCharArray();if (!isValid(exp)) {return 0;}//t[i][j]表示第i到j的exp中结果为true的结果数int[][] t = new int[exp.length][exp.length];//f[i][j]表示第i到j的exp中结果为false的结果数int[][] f = new int[exp.length][exp.length];t[0][0] = exp[0] == '0' ? 0 : 1;f[0][0] = exp[0] == '1' ? 0 : 1;//以i为尾,j为头,算t[i][j]与f[i][j]//依次向两边扩散,计算for (int i = 2; i < exp.length; i += 2) {//t[i][i]表示第i个位置为true的结果数//因此只要看是t[i][i]的值,值1返回1,值0返回0t[i][i] = exp[i] == '0' ? 0 : 1;f[i][i] = exp[i] == '1' ? 0 : 1;for (int j = i - 2; j >= 0; j -= 2) {//以k+1为分界,判断前面和后后的组成数目for (int k = j; k < i; k += 2) {if (exp[k + 1] == '&') {//k+1是&,结果是true的可能是前1后1t[j][i] += t[j][k] * t[k + 2][i];//k+1是&,结果是false的可能是前1后0,前0后1,前0后0//这里做了合并,先算前1后0和前0后0的结果,再加上前0后1f[j][i] += (f[j][k] + t[j][k]) * f[k + 2][i] + f[j][k] * t[k + 2][i];} else if (exp[k + 1] == '|') {t[j][i] += (f[j][k] + t[j][k]) * t[k + 2][i] + t[j][k] * f[k + 2][i];f[j][i] += f[j][k] * f[k + 2][i];} else {t[j][i] += f[j][k] * t[k + 2][i] + t[j][k] * f[k + 2][i];f[j][i] += f[j][k] * f[k + 2][i] + t[j][k] * t[k + 2][i];}}}}return desired ? t[0][t.length - 1] : f[0][f.length - 1];}