原题网址:https://leetcode.com/problems/different-ways-to-add-parentheses/
Given a string of numbers and operators, return all possible results from computing all the different possible ways to group numbers and operators. The valid operators are+
, -
and *
.
Example 1
Input: "2-1-1"
.
((2-1)-1) = 0 (2-(1-1)) = 2
Output: [0, 2]
Example 2
Input: "2*3-4*5"
(2*(3-(4*5))) = -34 ((2*3)-(4*5)) = -14 ((2*(3-4))*5) = -10 (2*((3-4)*5)) = -10 (((2*3)-4)*5) = 10
Output: [-34, -14, -10, -10, 10]
思路:开始想用深度优先搜索,发现无法处理,因为它并没有一个能够递增到达目标深度的路径。需要使用分而治之的策略,将表达式从某个运算符拆分为左右两段,递归计算两短的所有可能组合,然后对两段进行笛卡尔积。
优化要点:对字符串预处理,提取出运算符和操作数,以便加快后续的处理速度。
public class Solution {
private List<Integer> results = new ArrayList<>();
private List<Integer> compute(Expression[] expressions, int[] operands, int from, int to) {
List<Integer> results = new ArrayList<>();
if (from == to) {
results.add(operands[from]);
return results;
}
for(int i=from+1; i<to; i+=2) {
List<Integer> part1 = compute(expressions, operands, from, i-1);
List<Integer> part2 = compute(expressions,operands, i+1, to);
for(int j=0; j<part1.size(); j++) {
for(int k=0; k<part2.size(); k++) {
if (expressions[i]==Expression.PLUS) {
results.add(part1.get(j)+part2.get(k));
} else if (expressions[i]==Expression.MINUS) {
results.add(part1.get(j)-part2.get(k));
} else if (expressions[i]==Expression.MULTIPLY) {
results.add(part1.get(j)*part2.get(k));
} else {
// expressions[i]==Expression.DIVIDE
results.add(part1.get(j)/part2.get(k));
}
}
}
}
return results;
}
public List<Integer> diffWaysToCompute(String input) {
int operatorCount = 0;
char[] sa = input.toCharArray();
for(int i=0; i<sa.length; i++) {
if (sa[i]<'0' || sa[i]>'9') operatorCount++;
}
int[] operands = new int[operatorCount+operatorCount+1];
Expression[] expressions = new Expression[operatorCount+operatorCount+1];
int j=0;
for(int i=0; i<sa.length; i++) {
if (sa[i]=='+') expressions[j++]=Expression.PLUS;
else if (sa[i]=='-') expressions[j++]=Expression.MINUS;
else if (sa[i]=='*') expressions[j++]=Expression.MULTIPLY;
else if (sa[i]=='/') expressions[j++]=Expression.DIVIDE;
else {
int num=sa[i]-'0';
while (i<sa.length-1 && sa[i+1] >= '0' && sa[i+1] <= '9') num = num*10+sa[++i]-'0';
expressions[j]=Expression.OPERAND;
operands[j]=num;
j++;
}
}
return compute(expressions, operands, 0, expressions.length-1);
}
}
enum Expression {OPERAND, PLUS, MINUS, MULTIPLY, DIVIDE};
由于ArrayList操作比较耗时,快捷的方法是提前计算出组合个数,然后创建数组来存放,这个组合个数可以利用递归函数计算出来。
public class Solution {
private int[] ways;
private int[] compute(Expression[] expressions, int[] operands, int from, int to) {
int[] results = new int[ways[(to-from)/2]];
if (from == to) {
results[0] = operands[from];
return results;
}
int pos = 0;
for(int i=from+1; i<to; i+=2) {
int[] part1 = compute(expressions, operands, from, i-1);
int[] part2 = compute(expressions,operands, i+1, to);
for(int j=0; j<part1.length; j++) {
for(int k=0; k<part2.length; k++) {
if (expressions[i]==Expression.PLUS) {
results[pos++] = part1[j]+part2[k];
} else if (expressions[i]==Expression.MINUS) {
results[pos++] = part1[j]-part2[k];
} else if (expressions[i]==Expression.MULTIPLY) {
results[pos++] = part1[j]*part2[k];
} else {
// expressions[i]==Expression.DIVIDE
results[pos++] = part1[j]/part2[k];
}
}
}
}
return results;
}
public List<Integer> diffWaysToCompute(String input) {
int operatorCount = 0;
char[] sa = input.toCharArray();
for(int i=0; i<sa.length; i++) {
if (sa[i]<'0' || sa[i]>'9') operatorCount++;
}
int[] operands = new int[operatorCount+operatorCount+1];
Expression[] expressions = new Expression[operatorCount+operatorCount+1];
int j=0;
for(int i=0; i<sa.length; i++) {
if (sa[i]=='+') expressions[j++]=Expression.PLUS;
else if (sa[i]=='-') expressions[j++]=Expression.MINUS;
else if (sa[i]=='*') expressions[j++]=Expression.MULTIPLY;
else if (sa[i]=='/') expressions[j++]=Expression.DIVIDE;
else {
int num=sa[i]-'0';
while (i<sa.length-1 && sa[i+1] >= '0' && sa[i+1] <= '9') num = num*10+sa[++i]-'0';
expressions[j]=Expression.OPERAND;
operands[j]=num;
j++;
}
}
ways = new int[operands.length+1];
ways[0] = 1;
ways[1] = 1;
for(int i=2; i<=operands.length; i++) {
for(int k=0; k<i; k++) ways[i] += ways[k]*ways[i-1-k];
}
int[] computed = compute(expressions, operands, 0, expressions.length-1);
List<Integer> results = new ArrayList<>(computed.length);
for(int i=0; i<computed.length; i++) results.add(computed[i]);
return results;
}
}
enum Expression {OPERAND, PLUS, MINUS, MULTIPLY, DIVIDE};
另外考虑到表达式会重复计算,即有子问题重叠,可以借用动态规划思想保存子问题结果。
public class Solution {
private int[] ways;
private boolean[][] visited;
private int[][][] cache;
private int[] compute(Expression[] expressions, int[] operands, int from, int to) {
if (visited[from][to]) return cache[from][to];
int[] results = new int[ways[(to-from)/2]];
if (from == to) {
results[0] = operands[from];
visited[from][to] = true;
cache[from][to] = results;
return results;
}
int pos = 0;
for(int i=from+1; i<to; i+=2) {
int[] part1 = compute(expressions, operands, from, i-1);
int[] part2 = compute(expressions,operands, i+1, to);
for(int j=0; j<part1.length; j++) {
for(int k=0; k<part2.length; k++) {
if (expressions[i]==Expression.PLUS) {
results[pos++] = part1[j]+part2[k];
} else if (expressions[i]==Expression.MINUS) {
results[pos++] = part1[j]-part2[k];
} else if (expressions[i]==Expression.MULTIPLY) {
results[pos++] = part1[j]*part2[k];
} else {
// expressions[i]==Expression.DIVIDE
results[pos++] = part1[j]/part2[k];
}
}
}
}
visited[from][to] = true;
cache[from][to] = results;
return results;
}
public List<Integer> diffWaysToCompute(String input) {
int operatorCount = 0;
char[] sa = input.toCharArray();
for(int i=0; i<sa.length; i++) {
if (sa[i]<'0' || sa[i]>'9') operatorCount++;
}
int[] operands = new int[operatorCount+operatorCount+1];
Expression[] expressions = new Expression[operatorCount+operatorCount+1];
int j=0;
for(int i=0; i<sa.length; i++) {
if (sa[i]=='+') expressions[j++]=Expression.PLUS;
else if (sa[i]=='-') expressions[j++]=Expression.MINUS;
else if (sa[i]=='*') expressions[j++]=Expression.MULTIPLY;
else if (sa[i]=='/') expressions[j++]=Expression.DIVIDE;
else {
int num=sa[i]-'0';
while (i<sa.length-1 && sa[i+1] >= '0' && sa[i+1] <= '9') num = num*10+sa[++i]-'0';
expressions[j]=Expression.OPERAND;
operands[j]=num;
j++;
}
}
ways = new int[operands.length+1];
ways[0] = 1;
ways[1] = 1;
for(int i=2; i<=operands.length; i++) {
for(int k=0; k<i; k++) ways[i] += ways[k]*ways[i-1-k];
}
visited = new boolean[expressions.length][expressions.length];
cache = new int[expressions.length][expressions.length][];
int[] computed = compute(expressions, operands, 0, expressions.length-1);
List<Integer> results = new ArrayList<>(computed.length);
for(int i=0; i<computed.length; i++) results.add(computed[i]);
return results;
}
}
enum Expression {OPERAND, PLUS, MINUS, MULTIPLY, DIVIDE};