为运算表达式设计优先级

给你一个由数字和运算符组成的字符串 expression ,按不同优先级组合数字和运算符,计算并返回所有可能组合的结果。你可以 按任意顺序 返回答案。

生成的测试用例满足其对应输出值符合 32 位整数范围,不同结果的数量不超过 104 。

  • 示例 1:

输入:expression = "2-1-1"

输出:[0,2]

解释:

((2-1)-1) = 0

(2-(1-1)) = 2

  • 示例 2:
  • 输入:expression = "23-45"
  • 输出:[-34,-14,-10,-10,10]

解释:

(2*(3-(4*5))) = -34

((23)-(45)) = -14

((2*(3-4))*5) = -10

(2*((3-4)*5)) = -10

(((2*3)-4)*5) = 10  

提示:

1 <= expression.length <= 20

expression 由数字和算符 '+'、'-' 和 '*' 组成。

输入表达式中的所有整数值在范围 [0, 99] 

方法一:动态规划(Java)

因为最终的答案是由一个个子问题(子表达式)的答案所构成,所以我们可以采用动态规划,将问题划分为一个个子问题来求解。

做出此题最关键的一步是要写出合理的动态规划递归式。第一想法是定义dp[i]表示前i个数计算的结果,这样定义我们很快会发现我们无法写出dp[i+1],因为它们相互包含,没有明确的界限。

比较好的递归式是定义dp[i][j]表示从第i个数开始到第j个数的表达式计算的结果,最终结果就是要求dp[0][N-1]。

首先我们将字符串分成digit、op、digit、op、digit、op、digit.....这样的序列,并且可知序列的长度是奇数个,所以子问题的最小长度为3(长度为1的digit不需要计算),也就是一个op运算需要至少三个元素(两个digit和一个op),下一个子问题的长度为当前子问题+2(加一个op和一个digit),所以我们可以从最小长度为3的子问题一步步求解最大长度的解。

时间复杂度:O(2^n)

空间复杂度:O(2^n)

方法二:分治(Go)

分治:定义最后一个生效的符号位置。比如 a+b+c+d,我们定义第二个加号,为最后的计算位置,则可以得到【a+b】+【c+d】,类似这样的格式。然后此时可以发现 A=a+b 是一个表达式,B=c+d也是一个表达式,他们可以分别计算出各自的结果,然后再通过这个加号计算 A+B,每组两部分的和就对应所有可能的结果

对于一个形如 x op y(op 为运算符,x 和 y 为数) 的算式而言,它的结果组合取决于 x 和 y 的结果组合数,而 x 和 y 又可以写成形如 x op y 的算式。

因此,该问题的子问题就是 x op y 中的 x 和 y:以运算符分隔的左右两侧算式解。

分治算法三步走:

  • 分解:按运算符分成左右两部分,分别求解
  • 解决:实现一个递归函数,输入算式,返回算式解
  • 合并:根据运算符合并左右两部分的解,得出最终解

时间复杂度:O(2^n)

空间复杂度:O(2^n)