282. Expression Add Operators
Given a string that contains only digits 0-9 and a target value, return all possibilities to add binary operators (not unary) +, -, or * between the digits so they evaluate to the target value.
Example 1:
Input: num = "123", target = 6
Output: ["1+2+3", "1*2*3"]
Example 2:
Input: num = "232", target = 8
Output: ["2*3+2", "2+3*2"]
Example 3:
Input: num = "105", target = 5
Output: ["1*0+5","10-5"]
Example 4:
Input: num = "00", target = 0
Output: ["0+0", "0-0", "0*0"]
Example 5:
Input: num = "3456237490", target = 9191
Output: []
方法1: backtracking
grandyang: http://www.cnblogs.com/grandyang/p/4814506.html
花花:https://www.youtube.com/watch?v=v05R1OIIg08
思路:
传说中的24点题,答案能抽丝剥茧事无巨细真的非常厉害。首先一串数字可以有很多种切分方法,也有三种运算符,每一种都要dfs下去,所以确定基本思路是backtracking,而backtracking的基本单位是 operator + operand。那么我们要传递给下一层dfs哪些参数呢?首先是operator:三种选择,operand:从当前位置开始要切多少个digit出来,所有这些的combination都要递归下去。除此以外,本层还要传递之前累计计算的结果,那么如果下面一层继续+/-运算,结果可以直接累计;但如果下一层想要*运算,就必须把前一个运算单位和再之前的分割开来。举个栗子:2 + 3 * 5 , 如果往下直接传 2 + 3的结果,乘法是无法继续的,必须告知下一层优先级运算的当前结果,也就是+3,才可能用5 - 3 + 3 * 5 = 17 计算。 如果是连乘的情况呢? 举个栗子:2 + 3 * 4 * 5, 首先5 - 3 + 3 * 4 = 14, 这时为了能连乘,我们要传递的乘法优先级tmp是3 * 4, 那么下一步可以14 - 12 + 12 * 5 = 62。总结来讲dfs应该有如下几个参数:
- num:input
- target:input
- curNum: 之前累计的结果:已经激进的累计了上一个单位
- diff:保留着所有乘法至今所累加的diff
- current:一个string来记录表达式
- result:遍历到底层时需要push current
易错点
- edge case: 第一个划分出来的数字必须以“+”为operator
- “00000”, “003”:这种切分出来的不合法数字要跳过
- num == target : 采用len = 1 to num.size()的方式cover
- 推送条件
- 乘法的递推计算
- stoi会造成overflow,要用stoll
Complexity
Time complexity: O(n * 4 ^ (n - 1)),参见花花的slide。本质上是在n个数字的n-1个间隙中插入“”,“+”,“-”,“* ”四种字符,空字符表示前后连成一个长数字。每一个这样的表达式都是在一次O(n)的遍历中搜出来的,所以是O(n * 4 ^ (n - 1))。如果选择在得到完整表达式之后用basic calculator的方法来evaluate,需要O(n^2)
Space complexity: O(n), 递归的深度最多是O(n),注意dfs在每一个时间点,都仅保存着当前的string。
class Solution {
public:
vector<string> addOperators(string num, int target) {
vector<string> result;
addHelper(num, target, 0, 0, "", result);
return result;
}
void addHelper(string num, int target, int diff, long curNum, string current, vector<string> & result) {
if (num.size() == 0 && curNum == target) {
result.push_back(current);
return;
}
for (int len = 1; len <= num.size(); len++) {
string cur = num.substr(0, len);
string next = num.substr(len);
if (cur[0] == '0' && cur.size() > 1) continue;
if (current.size() > 0) {
addHelper(next, target, stoll(cur), curNum + stoll(cur), current + "+" + cur, result);
addHelper(next, target, -stoll(cur), curNum - stoll(cur), current + "-" + cur, result);
addHelper(next, target, diff * stoll(cur), curNum - diff + diff * stoll(cur), current + "*" + cur, result);
}
else {
// 这里为什么要单独处理呢?第一是因为起始的数字前面不应该加符号,其次要加只能implicitly加“+”,如果“*”和前面的diff(初始化为0)不正确的抵消掉,会产生错误值
addHelper(next, target, stoll(cur), curNum + stoll(cur), cur, result);
}
}
return;
}
};