2

I am trying to understand what is time complexity of leetcode 241. Different ways to add parentheses. I have used memoization technique. My friend was asked in Google coding round, he couldn't give correct time and space complexity. I found same problem in Leetcode.

Problem: 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"

Output: [0, 2]

Explanation:

((2-1)-1) = 0

(2-(1-1)) = 2

Example 2:

Input: "2 * 3 - 4 * 5"

Output: [-34, -14, -10, -10, 10]

Explanation:

(2*(3-(4*5))) = -34

((23)-(45)) = -14

((2*(3-4))*5) = -10

(2*((3-4)*5)) = -10

(((2*3)-4)*5) = 10

Code:

import java.util.*;

class Solution {
    Map<String, List<Integer>> map = new HashMap<>(); 
    public List<Integer> diffWaysToCompute(String input) {
        if(map.containsKey(input)) {
            return map.get(input);
        }
        
        List<Integer> result = new ArrayList<>();
        int length = input.length();
        
        for(int i = 0; i< length; i++) {
            char character = input.charAt(i);
            if(isOperator(character)) {
                String part1 = input.substring(0, i);
                String part2 = input.substring(i + 1);
                List<Integer> part1Result = diffWaysToCompute(part1);
                List<Integer> part2Result = diffWaysToCompute(part2);
                computeAndStoreResult(input, result, i, part1Result, part2Result);
        
            }
        }
        //store in map...
        map.put(input, result);
        //this is when only one input is present. 
        // input 3 ==> nothing is added in result so add 3 ...
        if(result.size() == 0) {
            result.add(Integer.valueOf(input));
        }
        return result;
    }
    
    private boolean isOperator(char character) {
        return character == '-' || character == '*' || character == '+';
    }
    
    private void computeAndStoreResult(String input, List<Integer> result, int i, List<Integer> part1Result, List<Integer> part2Result) {
        for(Integer p1 : part1Result) {
            for(Integer p2 : part2Result) {
                int c= 0;
                switch (input.charAt(i)) {
                    case '+':
                        c = p1+p2;
                        break;
                    case '-':
                        c = p1-p2;
                        break;
                    case '*':
                        c = p1*p2;
                        break;
                }
                result.add(c);
            }
        }
    }
}

I have research on many sites could not find good explanation This is how recursive tree looks like: Techinque used is divide and conquer with memoization. enter image description here

Some useful links that I found.

https://www.cnblogs.com/yrbbest/p/5006196.html

https://just4once.gitbooks.io/leetcode-notes/content/leetcode/divide-and-conquer/241-different-ways-to-add-parentheses.html

sujit amin
  • 121
  • 3
  • 10

1 Answers1

2

Observation

  1. If an expression consists of a single number, then there's 1 way to place parentheses.
  2. If there's only 1 math operator in an expression, then there's only 1 way to place parentheses to obtain a relevant result. For example, for 3 + 4 it's (3 + 4).
  3. If there are 2 math operators in an expression, then there are 2 ways to place parentheses. For example, for 3 + 4 - 2, the method will split the expression by + into 3 and 4 - 2, and then by - into 3 + 4 and 2. So, it's 2 ways.
  4. If there are 3 math operators in an expression, then it's 5 results. For example, 1 + 2 - 3 * 4 can be split into 1 and 2 - 3 * 4, 1 + 2 and 3 * 4, 1 + 2 - 3 and 4. As we've learned from 2) and 3), the number of ways is 2 + 1 + 2 = 5` ways.
  5. If there are 4 math operators in an expression, then it's 14 results. For example, 1 + 2 - 3 * 4 + 5 can be split into 1 and 2 - 3 * 4 + 5, 1 + 2 and 3 * 4 + 5, 1 + 2 - 3 and 4 + 5, 1 + 2 - 3 * 4 and 5. As we've learned from 2), 3) and 4) the number of ways is 5 + 2 + 2 + 5 = 14 ways correspondingly.

If the sequence is continued then it will be 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, .... As you can see, it grows exponentially.

Such sequence of numbers has a name and is called Catalan numbers.

Application for your algorithm

As you may have already realised, your algorithm will become exponential even after you obtain results for your left and right subexpressions split by the first math operator.

So, if there are 10 math operators, then after computing the results for the first configuration (expression is split by the first math operator) their number will be 4862.

So, not only is your time complexity exponential, but also your space complexity since you save all the results in a list.

Anatolii
  • 14,139
  • 4
  • 35
  • 65