Home > Back-end >  Finding all possible combinations of numbers of an array to reach a given sum
Finding all possible combinations of numbers of an array to reach a given sum

Time:02-17

We are provided an array of N numbers and a target value k. We need to find all possible ways to form an expression by inserting the operators ( ,- and *) in such a way that the expression evaluates to the value k.
For example:

  • Input: Array = {3,4,3}, k = 15
  • Output: 3*4 3, 3 4*3

I couldn't develop a recursive solution to this problem. With just ' ' and '-' operator, it would be easier but because of '*' operator it becomes difficult to use recursive approach as we also need to care about the precendency of operators. Can you help me finding a suitable approach to solve this problem?

CodePudding user response:

Took me while to code this. So it's basically brute force. I recursively (backtracking) generate all possible expression with the operators given and then evaluate them. Note these are just infix expression(s).

Now this is a very slow solution. There are several optimization one can do here.

vector<string> allCombinations(vector<int> &arr, int k)
{
    int n = (int)arr.size();
    string operators = " -*";
    vector<string> ans;
    // To check precedence of operators
    auto prec = [&](char op) -> int
    {
        if (op == '*' or op == '/') return 2;
        if (op == ' ' or op == '-') return 1;
        return -1;
    };
    // For infix evaluation (kindof a helper function)
    auto compute = [&](int v1, char op, int v2) -> int
    {
        if (op == ' ') return v1   v2;
        if (op == '-') return v1 - v2;
        if (op == '*') return v1 * v2;
        if (op == '/') return v1 / v2;
        assert(false);
        return INT_MAX;
    };
    // Main infix evaluation function
    auto evaluate = [&](string s) -> int
    {
        int len = (int)s.size();
        // vector is being used as a STACK
        vector<int> val;
        vector<char> ops;
        for (int i = 0; i < len; i  )
        {
            char curr = s[i];
            if (curr == ' ') continue;
            if (isdigit(curr))
            {
                int v = 0;
                while (i < len and isdigit(s[i])) v = 10 * v   (s[i  ] - '0');
                val.push_back(v);
                i--;
            }
            else
            {
                while (!ops.empty() and prec(curr) <= prec(ops.back()))
                {
                    int v1 = val.back();
                    val.pop_back();
                    int v2 = val.back();
                    val.pop_back();
                    char op = ops.back();
                    ops.pop_back();
                    val.push_back(compute(v2, op, v1));
                }
                ops.push_back(curr);
            }
        }
        while (!ops.empty())
        {
            int v1 = val.back();
            val.pop_back();
            int v2 = val.back();
            val.pop_back();
            char op = ops.back();
            ops.pop_back();
            val.push_back(compute(v2, op, v1));
        }
        return val.back();
    };
    // Generates all expression possible
    function<void(int, string&)> generate = [&](int i, string &s) -> void
    {
        s  = to_string(arr[i]);
        if (i == n - 1)
        {
            if (evaluate(s) == k) ans.push_back(s);
            // Backtrack
            s.pop_back();
            return;
        }
        for (char &ops : operators) 
        {
            s.push_back(ops);
            generate(i   1, s);
            // Backtrack
            s.pop_back();
        }
        // Backtrack
        s.pop_back();
    };
    string s;
    // Try all combinations
    sort(arr.begin(), arr.end());
    do
    {
        generate(0, s);
    } while (next_permutation(arr.begin(), arr.end()));
    return ans;
}

CodePudding user response:

Finding all combinations of operators is pretty simple. If you represent each operator as a digit of a zero-prefixed number in base-3, then you can create a function that renders that base-3 number from an integer, but uses {' ','-','*'} instead of {'0','1','2'} for the digits.

private static final char[] CHARS = " -*".toCharArray();
private char[] generateOperators(int i, int length){
    char[] ops=new char[length];
    for (int x=0; x<length; x  ) ops[x]=CHARS[0];
    for (int x=0; x<length; x  ) {
        ops[length-1-x]=CHARS[i % CHARS.length];
        i /= CHARS.length;
    }
    return ops;
}

You can accumulate your total left to right without backtracking. Keep a total and a separate accumulator. Any time your next operand is a ' ' or '-', it's safe to push your accumulated value to the total. If it's a '*', then you know the current operand is participating in the next accumulation. In this function, I essentially treat every run of multiplied operands as a cluster, treating even single values as a cluster of one, and push that accumulated value to the total when I reach the end of a cluster.

public int evaluate(int[] operands, char[] operators) {
    int sum=0;
    int multiplier=1;
    for (int i=0; i<operators.length; i  ) {
        int operand=operands[i];
        multiplier*=operand;
        if (operators[i]!='*') {
            sum =multiplier;
            multiplier=operators[i]=='-' ? -1 : 1;
        }
    }
    if (operators[operators.length-1]=='*') {
        multiplier*=operands[operands.length-1];
    } else {
        multiplier=(operators[operators.length-1]=='-' ? -1 : 1) * operands[operands.length-1];
    }
    sum =multiplier;
    return sum;
}
  • Related