🚩传送门: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本身的特性来形式上降维。
