🚩传送门:https://leetcode-cn.com/problems/different-ways-to-add-parentheses/
题目
给定一个含有数字和运算符的字符串,为表达式添加括号,改变其运算优先级以求出不同的结果。你需要给出所有可能的组合的结果。有效的运算符号包含 +
, -
以及 *
。
示例 1:
输入: “2-1-1” 输出: [0, 2] 解释: ((2-1)-1) = 0 (2-(1-1)) = 2
示例 2:
输入: “23-45” 输出: [-34, -14, -10, -10, 10] 解释: 2 -2 -5 -17 10 -10 -10 -34 (2(3-(45))) = -34 ((23)-(45)) = -14 ((2(3-4))5) = -10 (2((3-4)5)) = -10 (((23)-4)5) = 10
解题思路:分治
分治算法三步走:
- 分解:分成部分,分别求解
- 解决:实现一个递归函数,输入算式,返回算式解
- 合并:合并部分的解,得出最终解
本题解 采用了 分治 的思想:
- [分解]:遍历字符串,遇到操作符,就将左右两边的字符串,分别当作 两个表达式
- [解决]:带入算式,求解两侧的答案集合
- [合并]:将两侧的答案集合通过当前操作符组合成心的答案集合。
简洁的说明代码:
class Solution:
def diffWaysToCompute(self, input: str) -> List[int]:
# 如果只有数字,直接返回
if input.isdigit():
return [int(input)]
res = []
# 依次遍历字符串,读取运算符
for i, char in enumerate(input):
if char in ['+', '-', '*']:
# 1.分解:遇到运算符,计算左右两侧的结果集
# 目的:解决diffWaysToCompute 递归函数求出子问题的解
left = self.diffWaysToCompute(input[:i]) #left得到的是左侧结果集
right = self.diffWaysToCompute(input[i+1:]) #right得到的是右侧结果集
# 2.合并:根据运算符合并子问题的解
for l in left:
for r in right:
if char == '+':
res.append(l + r)
elif char == '-':
res.append(l - r)
else:
res.append(l * r)
return res
解题思路:动态规划
dp
数组的含义比较难定义,需要做一个预处理,把字符串中的每个数字和运算符分开存起来 。
这样的话我们就有了两个
list
,一个保存了所有数字,一个保存了所有运算符。
举例子:2 3 - 4 5
存起来的数字是 numList = [2345] 存起来的运算符是 opList = [, -, ]
👉 定义 含义是第 到第 个数字(从 0 开始计数)范围内的表达式的所有解 。
举例子:2 3 - 4 5
dp[1][3] 就代表第一个数字 3 到第三个数字 5 范围内的表达式 3 - 4 * 5 的所有解。
✍初始条件,就是范围内只有一个数字。
举例子:2 3 - 4 5
dp[0][0] = [2],dp[1][1] = [3],dp[2][2] = [4],dp[3][3] = [5]。
package dp.dp241;
import java.util.ArrayList;
import java.util.List;
public class demo {
public static List<Integer> diffWaysToCompute(String input) {
List<Integer> numList = new ArrayList<>();
List<Character> opList = new ArrayList<>();
char[] array = input.toCharArray();
int num = 0;
//=预处理部分=
for (int i = 0; i < array.length; i++) {
if (isOperation(array[i])) {
//由于之前处理的整数并未存储,此处存储
numList.add(num);
num = 0;
opList.add(array[i]);
continue;
}
//整数处理
num = array[i] - '0';
}
numList.add(num);
int N = numList.size(); // 数字的个数
// 初始条件:一个数字
ArrayList<Integer>[][] dp = (ArrayList<Integer>[][]) new ArrayList[N][N];
for (int i = 0; i < N; i++) {
ArrayList<Integer> result = new ArrayList<>();
result.add(numList.get(i));
dp[i][i] = result;
}
// 2 个数字到 N 个数字
for (int n = 2; n <= N; n++) { //n代表每次处理字段的长度
for (int i = 0; i < N; i++) { // 开始下标
int j = i + n - 1; // 结束下标
if (j >= N) { //下标越界
break;
}
ArrayList<Integer> result = new ArrayList<>();
// 分成 i ~ s 和 s+1 ~ j 两部分,处理的字段,遍历字段起始到终止
for (int s = i; s < j; s++) {
ArrayList<Integer> result1 = dp[i][s];
ArrayList<Integer> result2 = dp[s + 1][j];
//分成两段的拼接
for (int x = 0; x < result1.size(); x++) {
for (int y = 0; y < result2.size(); y++) {
// 第 s 个数字下标对应是第 s 个运算符
char op = opList.get(s);
result.add(caculate(result1.get(x), op, result2.get(y)));
}
}
}
dp[i][j] = result;
}
}
return dp[0][N-1];
}
//计算
private static int caculate(int num1, char c, int num2) {
switch (c) {
case '+':
return num1 + num2;
case '-':
return num1 - num2;
case '*':
return num1 * num2;
}
return -1;
}
private static boolean isOperation(char c) {
return c == '+' || c == '-' || c == '*';
}
}
解释:
ArrayList<Integer>[][] dp
是三维数组,可以利用ArrayList
本身的特性来形式上降维。