Problem Description:
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
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]
Analysis:
The problem seems to be little hard at first glance. However the hashtag hint that it’s an easy way to apply divide and conquer method.
The idea is : when encounter an operator, we split the string to two parts. One left to the operator and the other right to it. Then we recursively compute the all possible result for each part. At last we just need combine the two parts’ result based on the operator. Careful the string only contains operand.
Recursive:
vector<int> diffWaysToCompute(string input) {
vector<int> res;
int n = input.size();
for (int i = 0; i < n; i++ )
{
char c = input[i];
if (ispunct(c)){
// Split input string into two parts and solve them recursively
vector<int> res1 = diffWaysToCompute(input.substr(0, i));
vector<int> res2 = diffWaysToCompute(input.substr(i+1));
for (auto op1 : res1)
for (auto op2 : res2){
if (c == '+')
res.push_back(op1 + op2);
else if (c == '-')
res.push_back(op1 - op2);
else
res.push_back(op1 * op2);
}
}
}
if (res.empty())
//res.push_back(atoi(input.c_str());
res.push_back(stoi(input));
return res;
}
Top down & Memorization method (not dp) :
class Solution {
public:
vector<int> diffWaysToCompute(string input)
{
unordered_map <string, vector<int> > memo;
return Compute(memo, input);
}
vector<int> Compute(unordered_map<string, vector<int> > & memo, string input)
{
vector<int> res;
for (int i = 0; i < input.size(); ++i)
{
vector<int> res1, res2;
if (ispunct(input[i]))
{
string left = input.substr(0, i);
if (memo[left].size() > 0)
res1 = memo[left];
else
res1 = Compute(memo, left);
string right = input.substr(i + 1);
if (memo[right].size() > 0)
res2 = memo[right];
else
res2 = Compute(memo, right);
for (auto r1 : res1)
for (auto r2 : res2)
{
if (input[i] == '+')
res.push_back(r1 + r2);
else if (input[i] == '-')
res.push_back(r1 - r2);
else res.push_back(r1 * r2);
}
}
}
if (res.empty())
res.push_back(stoi(input));
memo[input] = res;
return res;
}
};
Also we could use Dynamic programming to solve the problem:
Bottom-up solution.
vector<int> diffWaysToCompute(string input) {
vector<int> data;
vector<char> ops;
int num = 0;
char op = ' ';
istringstream ss(input + "+");
while(ss >> num && ss >> op){
data.push_back(num);
ops.push_back(op);
}
const int size = data.size();
vector< vector<vector<int> > > dp(size, vector<vector<int> >(size, vector<int>()));
for (int i = 0; i < size; i += 1)
for (int j = i; j >= 0; j -= 1){
if(i == j) {dp[j][i].push_back(data[i]); continue;}
for (int k = j; k < i; k += 1){
for (auto left : dp[j][k])
for (auto right : dp[k+1][i]){
int val = 0;
switch(ops[k]){
case '+': val = left + right; break;
case '-': val = left - right; break;
case '*': val = left * right; break;
}
dp[j][i].push_back(val);
}
}
}
return dp[0][size-1];
}