Given a string of numbers and a number of multiplication operators, what is the highest number one can calculate?

后端 未结 9 1550
醉话见心
醉话见心 2021-01-30 11:22

This was an interview question I had and I was embarrassingly pretty stumped by it. Wanted to know if anyone could think up an answer to it and provide the big O notation for it

相关标签:
9条回答
  • 2021-01-30 11:56

    This came to mind, it's the brute force approach influenced by the bars and stars problem.

    Let's say our number is "12345" and we have 2 * operators we need to use. We can look at the string 12345 as

    1_2_3_4_5
    

    Where we can put the two * operators on any of the underscores. Since there are 4 underscores and 2 * operators, there are 4 choose 2 (or 6) different ways to place the operators. Compare those 6 possibilities and grab the largest number. A similar approach can be used for larger strings and larger number of * operators.

    0 讨论(0)
  • 2021-01-30 11:57

    here's another Java solution. (I know it's correct for "312" and 1 multiplication and I think it works for others...

    You'll have to remember how to obtain the complexity of recursive methods on your own, haha.

    package test;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class BiggestNumberMultiply {
    
        private static class NumberSplit{
            String[] numbers;
            long result;
            NumberSplit(String[] numbers){
                this.numbers=numbers.clone();
                result=1;
                for(String n:numbers){
                    result*=Integer.parseInt(n);
                }
            }
            @Override
            public String toString() {
                StringBuffer sb=new StringBuffer();
                for(String n:numbers){
                    sb.append(n).append("*");
                }
                sb.replace(sb.length()-1, sb.length(), "=")
                    .append(result);
                return sb.toString();
            }
        }
    
        public static void main(String[] args) {
            String numbers = "312";
            int numMults=1;
    
            int numSplits=numMults;
    
            List<NumberSplit> splits = new ArrayList<NumberSplit>();
            splitNumbersRecursive(splits, new String[numSplits+1], numbers, numSplits);
            NumberSplit maxSplit = splits.get(0);
            for(NumberSplit ns:splits){
                System.out.println(ns);
                if(ns.result>maxSplit.result){
                    maxSplit = ns;
                }
            }
            System.out.println("The maximum is "+maxSplit);
        }
    
        private static void splitNumbersRecursive(List<NumberSplit> list, String[] splits, String numbers, int numSplits){
            if(numSplits==0){
                splits[splits.length-1] = numbers;
                return;
            }
            for(int i=1; i<=numbers.length()-numSplits; i++){
                splits[splits.length-numSplits-1] = numbers.substring(0,i);
                splitNumbersRecursive(list, splits, numbers.substring(i), numSplits-1);
                list.add(new NumberSplit(splits));
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-30 12:05

    I found the above DP solution helpful but confusing. The recurrence makes some sense, but I wanted to do it all in one table without that final check. It took me ages to debug all the indices, so I've kept some explanations.

    To recap:

    1. Initialize T to be of size N (because digits 0..N-1) by k+1 (because 0..k multiplications).
    2. The table T(i,j) = the greatest possible product using the i+1 first digits of the string (because of zero indexing) and j multiplications.
    3. Base case: T(i,0) = digits[0..i] for i in 0..N-1.
    4. Recurrence: T(i,j) = maxa(T(a,j-1)*digits[a+1..i]). That is: Partition digits[0..i] in to digits[0..a]*digits[a+1..i]. And because this involves a multiplication, the subproblem has one fewer multiplications, so search the table at j-1.
    5. In the end the answer is stored at T(all the digits, all the multiplications), or T(N-1,k).

    The complexity is O(N2k) because maximizing over a is O(N), and we do it O(k) times for each digit (O(N)).

    public class MaxProduct {
    
        public static void main(String ... args) {
            System.out.println(solve(args[0], Integer.parseInt(args[1])));
        }
    
        static long solve(String digits, int k) {
            if (k == 0)
                return Long.parseLong(digits);
    
            int N = digits.length();
            long[][] T = new long[N][k+1];
            for (int i = 0; i < N; i++) {
                T[i][0] = Long.parseLong(digits.substring(0,i+1));
                for (int j = 1; j <= Math.min(k,i); j++) {
                    long max = Integer.MIN_VALUE;
                    for (int a = 0; a < i; a++) {
                        long l = Long.parseLong(digits.substring(a+1,i+1));
                        long prod = l * T[a][j-1];
                        max = Math.max(max, prod);
                    }
                    T[i][j] = max;
                }
            }
            return T[N-1][k];
        }
    }
    
    0 讨论(0)
  • 2021-01-30 12:06

    I am assuming here that the required number m of multiplication operators is given as part of the problem, along with the string s of digits.

    You can solve this problem using the tabular method (aka "dynamic programming") with O(m |s|2) multiplications of numbers that are O(|s|) digits long. The optimal computational complexity of multiplication is not known, but with the schoolbook multiplication algorithm this is O(m |s|4) overall.

    (The idea is to compute the answer for each subproblem consisting of a tail of the string and a number m′ ≤ m. There are O(m |s|) such subproblems and solving each one involves O(|s|) multiplications of numbers that are O(|s|) digits long.)

    In Python, you could program it like this, using the @memoized decorator from the Python decorator library:

    @memoized
    def max_product(s, m):
        """Return the maximum product of digits from the string s using m
        multiplication operators.
    
        """
        if m == 0:
            return int(s)
        return max(int(s[:i]) * max_product(s[i:], m - 1)
                   for i in range(1, len(s) - m + 1))
    

    If you're used to the bottom-up form of dynamic programming where you build up a table, this top-down form might look strange, but in fact the @memoized decorator maintains the table in the cache property of the function:

    >>> max_product('56789', 1)
    51102
    >>> max_product.cache
    {('89', 0): 89, ('9', 0): 9, ('6789', 0): 6789, ('56789', 1): 51102, ('789', 0): 789}
    
    0 讨论(0)
  • 2021-01-30 12:06

    This implementation is for @lars.

    from __future__ import (print_function)
    import collections
    import sys
    
    try:
        xrange
    except NameError:  # python3
        xrange = range
    
    
    def max_product(s, n):
        """Return the maximum product of digits from the string s using m
        multiplication operators.
    
        """
        # Guard condition.
        if len(s) <= n:
            return None
    
        # A type for our partial solutions.
        partial_solution = collections.namedtuple("product",
                                                  ["value", "expression"])
    
        # Initialize the best_answers dictionary with the leading terms
        best_answers = {}
        for i in xrange(len(s)):
            term = s[0: i+1]
            best_answers[i+1] = partial_solution(int(term), term)
    
        # We then replace best_answers n times.
        for prev_product_count in [x for x in xrange(n)]:
            product_count = prev_product_count + 1
            old_best_answers = best_answers
            best_answers = {}
            # For each position, find the best answer with the last * there.
            for position in xrange(product_count+1, len(s)+1):
                candidates = []
                for old_position in xrange(product_count, position):
                    prior_product = old_best_answers[old_position]
                    term = s[old_position:position]
                    value = prior_product.value * int(term)
                    expression = prior_product.expression + "*" + term
                    candidates.append(partial_solution(value, expression))
                # max will choose the biggest value, breaking ties by the expression
                best_answers[position] = max(candidates)
    
        # We want the answer with the next * going at the end of the string.
        return best_answers[len(s)]
    
    print(max_product(sys.argv[1], int(sys.argv[2])))
    

    Here is a sample run:

    $ python mult.py 99287 2
    product(value=72036, expression='9*92*87')
    

    Hopefully the logic is clear from the implementation.

    0 讨论(0)
  • 2021-01-30 12:11

    Yet another Java implementation. This is DP top down, aka memoization. It also prints out the actual components besides the max product.

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class MaxProduct {
    
        private static Map<Key, Result> cache = new HashMap<>();
    
        private static class Key {
            int operators;
            int offset;
    
            Key(int operators, int offset) {
                this.operators = operators;
                this.offset = offset;
            }
    
            @Override
            public int hashCode() {
                final int prime = 31;
                int result = 1;
                result = prime * result + offset;
                result = prime * result + operators;
                return result;
            }
    
            @Override
            public boolean equals(Object obj) {
                if (this == obj) {
                    return true;
                }
                if (obj == null) {
                    return false;
                }
                if (!(obj instanceof Key)) {
                    return false;
                }
                Key other = (Key) obj;
                if (offset != other.offset) {
                    return false;
                }
                if (operators != other.operators) {
                    return false;
                }
                return true;
            }
        }
    
        private static class Result {
            long product;
            int offset;
            Result prev;
    
            Result (long product, int offset) {
                this.product = product;
                this.offset = offset;
            }
    
            @Override
            public String toString() {
                return "product: " + product + ", offset: " + offset;
            }
        }
    
        private static void print(Result result, String input, int operators) {
            System.out.println(operators + " multiplications on: " + input);
            Result current = result;
            System.out.print("Max product: " + result.product + " = ");
            List<Integer> insertions = new ArrayList<>();
            while (current.prev != null) {
                insertions.add(current.offset);
                current = current.prev;
            }
    
            List<Character> inputAsList = new ArrayList<>();
            for (char c : input.toCharArray()) {
                inputAsList.add(c);
            }
    
            int shiftedIndex = 0;
            for (int insertion : insertions) {
                inputAsList.add(insertion + (shiftedIndex++), '*');
            }
    
            StringBuilder sb = new StringBuilder();
            for (char c : inputAsList) {
                sb.append(c);
            }
    
            System.out.println(sb.toString());
            System.out.println("-----------");
        }
    
        public static void solve(int operators, String input) {
            cache.clear();
            Result result = maxProduct(operators, 0, input);
            print(result, input, operators);
        }
    
        private static Result maxProduct(int operators, int offset, String input) {
            String rightSubstring = input.substring(offset);
    
            if (operators == 0 && rightSubstring.length() > 0) return new Result(Long.parseLong(rightSubstring), offset);
            if (operators == 0 && rightSubstring.length() == 0) return new Result(1, input.length() - 1);
    
            long possibleSlotsForFirstOperator = rightSubstring.length() - operators;
            if (possibleSlotsForFirstOperator < 1) throw new IllegalArgumentException("too many operators");
    
            Result maxProduct = new Result(-1, -1);
            for (int slot = 1; slot <= possibleSlotsForFirstOperator; slot++) {
                long leftOperand = Long.parseLong(rightSubstring.substring(0, slot));
                Result rightOperand;
                Key key = new Key(operators - 1, offset + slot);
                if (cache.containsKey(key)) {
                    rightOperand = cache.get(key);
                } else {
                    rightOperand = maxProduct(operators - 1, offset + slot, input);
                }
    
                long newProduct = leftOperand * rightOperand.product;
                if (newProduct > maxProduct.product) {
                    maxProduct.product = newProduct;
                    maxProduct.offset = offset + slot;
                    maxProduct.prev = rightOperand;
                }
            }
    
            cache.put(new Key(operators, offset), maxProduct);
            return maxProduct;
        }
    
        public static void main(String[] args) {
            solve(5, "1826456903521651");
            solve(1, "56789");
            solve(1, "99287");
            solve(2, "99287");
            solve(2, "312");
            solve(1, "312");
        }
    
    }
    

    Bonus: a bruteforce implementation for anyone interested. Not particularly clever but it makes the traceback step straightforward.

    import java.util.ArrayList;
    import java.util.List;
    
    public class MaxProductBruteForce {
    
        private static void recurse(boolean[] state, int pointer, int items, List<boolean[]> states) {
            if (items == 0) {
                states.add(state.clone());
                return;
            }
    
            for (int index = pointer; index < state.length; index++) {
                state[index] = true;
                recurse(state, index + 1, items - 1, states);
                state[index] = false;
            }
        }
    
        private static List<boolean[]> bruteForceCombinations(int slots, int items) {
            List<boolean[]> states = new ArrayList<>(); //essentially locations to insert a * operator
            recurse(new boolean[slots], 0, items, states);
            return states;
        }
    
        private static class Tuple {
            long product;
            List<Long> terms;
    
            Tuple(long product, List<Long> terms) {
                this.product = product;
                this.terms = terms;
            }
    
            @Override
            public String toString() {
                return product + " = " + terms.toString();
            }
        }
    
        private static void print(String input, int operators, Tuple result) {
            System.out.println(operators + " multiplications on: " + input);
            System.out.println(result.toString());
            System.out.println("---------------");
        }
    
        public static void solve(int operators, String input) {
            Tuple result = maxProduct(input, operators);
            print(input, operators, result);
        }
    
        public static Tuple maxProduct(String input, int operators) {
            Tuple maxProduct = new Tuple(-1, null);
    
            for (boolean[] state : bruteForceCombinations(input.length() - 1, operators)) {
                Tuple newProduct = getProduct(state, input);
                if (maxProduct.product < newProduct.product) {
                    maxProduct = newProduct;
                }
            }
    
            return maxProduct;
        }
    
        private static Tuple getProduct(boolean[] state, String input) {
            List<Long> terms = new ArrayList<>();
            List<Integer> insertLocations = new ArrayList<>();
            for (int i = 0; i < state.length; i++) {
                if (state[i]) insertLocations.add(i + 1);
            }
    
            int prevInsert = 0;
            for (int insertLocation : insertLocations) {
                terms.add(Long.parseLong(input.substring(prevInsert, insertLocation))); //gradually chop off the string
                prevInsert = insertLocation;
            }
    
            terms.add(Long.parseLong(input.substring(prevInsert))); //remaining of string
    
            long product = 1;
            for (long term : terms) {
                product = product * term;
            }
    
            return new Tuple(product, terms);
        }
    
        public static void main(String[] args) {
            solve(5, "1826456903521651");
            solve(1, "56789");
            solve(1, "99287");
            solve(2, "99287");
            solve(2, "312");
            solve(1, "312");
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题