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 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]
Analysis:
It’s easy to think recursively using divide and conquer method to solve the problem:
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;
}
Since there are lots of repeated subproblem, so we can use memo to record the result which we computed.
Memorization (top - down solution):
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;
}
We also can think it bottom up, using DP to solve it.
DP[i][j]means the result between operand i and operand j, Moreover we need to store the operators correspondingly
vector<int> diffWaysToCompute(string input) {
vector<int> data;
vector<char> ops;
int num = 0;
char op = ' ';
istringstream ss(input + "+");//add extra '+';
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];
}