本题的核心在于分治思想,即将一整个大问题的求解分治为很多小问题,最后再根据逻辑将其合并。dp[i][j][k] [i,j]区间运算结果为0或1的方法数,k=0或1,可由递推得到,初始化是对单个运算数进行初始化,为0的话dp[i][j][0]取1,反之取0。具体进行操作时按子数组长度由小到大的顺序遍历,每轮求出每个长度作为窗口在数组上滑动可以取的所有值。具体的合并过程在于将两边的所有可能情况相乘求出笛卡尔积,两边的子数组的长度都是比当前子数组长度短的子数组,在之前的循环中就已经求出了,现在刚好可以用上。
以下是示例代码:
package main
var dp [][][]int
/*
*dp[i][j][k] [i,j]区间运算结果为0或1的方法数,k=0或1,可由递推得到
*长度由3开始到n,i = 0 ~ i = n-1,共(n+1)/2个值。
*/
func countEval(s string, result int) int {
n := len(s)
num := (n + 1) / 2
dp := make([][][]int, num)
for i := 0; i < num; i++ {
dp[i] = make([][]int, num)
for j := 0; j < num; j++ {
dp[i][j] = make([]int, 2)
}
}
for i := 0; i < n; i += 2 {
if s[i] == '0' {
dp[i/2][i/2][0] = 1
dp[i/2][i/2][1] = 0
} else {
dp[i/2][i/2][0] = 0
dp[i/2][i/2][1] = 1
}
}
for len := 3; len <= n; len += 2 {
//长度为len的子串
for i := 0; i <= n-len; i += 2 {
//长度为len的子串的起始位置,i+len-1 <= n-1,i <= n-len
j := i + len - 1
for split := i + 1; split < j; split += 2 {
//[i,j]当中可取分隔符的位置
switch s[split] {
case '&':
dp[i/2][j/2][0] += dp[i/2][(split-1)/2][0] * dp[(split+1)/2][j/2][0]
dp[i/2][j/2][0] += dp[i/2][(split-1)/2][0] * dp[(split+1)/2][j/2][1]
dp[i/2][j/2][0] += dp[i/2][(split-1)/2][1] * dp[(split+1)/2][j/2][0]
dp[i/2][j/2][1] += dp[i/2][(split-1)/2][1] * dp[(split+1)/2][j/2][1]
case '|':
dp[i/2][j/2][0] += dp[i/2][(split-1)/2][0] * dp[(split+1)/2][j/2][0]
dp[i/2][j/2][1] += dp[i/2][(split-1)/2][0] * dp[(split+1)/2][j/2][1]
dp[i/2][j/2][1] += dp[i/2][(split-1)/2][1] * dp[(split+1)/2][j/2][0]
dp[i/2][j/2][1] += dp[i/2][(split-1)/2][1] * dp[(split+1)/2][j/2][1]
case '^':
dp[i/2][j/2][0] += dp[i/2][(split-1)/2][0] * dp[(split+1)/2][j/2][0]
dp[i/2][j/2][1] += dp[i/2][(split-1)/2][0] * dp[(split+1)/2][j/2][1]
dp[i/2][j/2][1] += dp[i/2][(split-1)/2][1] * dp[(split+1)/2][j/2][0]
dp[i/2][j/2][0] += dp[i/2][(split-1)/2][1] * dp[(split+1)/2][j/2][1]
default:
}
}
}
}
return dp[0][(n-1)/2][result]
}