给定一串数字和多个乘法运算符,可以计算出的最大数字是多少?
Posted
技术标签:
【中文标题】给定一串数字和多个乘法运算符,可以计算出的最大数字是多少?【英文标题】:Given a string of numbers and a number of multiplication operators, what is the highest number one can calculate? 【发布时间】:2013-10-08 05:44:27 【问题描述】:这是我遇到的一个面试问题,我非常尴尬地被它难住了。想知道是否有人可以想出一个答案并为其提供大 O 符号。
Question: Given a string of numbers and a number of multiplication operators,
what is the highest number one can calculate? You must use all operators
您不能重新排列字符串。您只能使用乘法运算符来计算一个数字。
例如String = "312"
,1个乘法运算符
您可以使用3*12 = 36
或31*2= 62
。后者显然是正确的答案。
【问题讨论】:
只有乘法运算符?31!^2
很大...
是的,只有提供的乘法运算符的数量可用于计算更大的数字
您必须使用指定数量的运算符?否则,312
显然是正确答案。
据我了解,您必须使用所有运算符。
使用蛮力这是一个“n选择k”问题。
【参考方案1】:
此实现适用于@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])))
这是一个示例运行:
$ python mult.py 99287 2
product(value=72036, expression='9*92*87')
希望从实现中逻辑清晰。
【讨论】:
这条线在做什么:product_count = prev_product_count + 1? “product(value=72036, expression='9*92*87')”中定义的函数product在哪里?我不知道“最后*那里”和那里指的是什么?老实说,我并不真正关心代码,伪代码会很好并且可能更受欢迎。product_count
是*
出现在部分答案中的次数。所以prev_product_count
是最后一代的计数(范围从0
到n-1
),product_count
是这一代。至于product
,它是从对collections.namedtuple
的调用中定义的。在伪代码与真实代码之间,自下而上的解决方案自然有很多细节。如果您采用模糊的答案并尝试实施它,您将一遍又一遍地得到一个令人困惑的错误。【参考方案2】:
我发现上述 DP 解决方案很有帮助但令人困惑。重复出现是有道理的,但我想在没有最后检查的情况下在一张表中完成所有操作。我花了很长时间调试所有索引,所以我保留了一些解释。
回顾一下:
-
将 T 初始化为大小为 N(因为数字 0..N-1)乘以 k+1(因为 0..k 次乘法)。
表 T(i,j) = 使用字符串的前 i+1 个数字(由于零索引)和 j 次乘法得到的最大可能乘积。
基本情况:T(i,0) = digits[0..i] for i in 0..N-1。
重复:T(i,j) = maxa(T(a,j-1)*digits[a+1..i])。即:将digits[0..i]划分为digits[0..a]*digits[a+1..i]。并且因为这涉及到乘法,所以子问题的乘法次数少了一次,所以搜索 j-1 处的表。
最后,答案存储在 T(所有数字,所有乘法)或 T(N-1,k)。
复杂度为 O(N2k),因为在 a 上最大化是 O(N),我们对每个数字执行 O(k) 次 (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];
【讨论】:
【参考方案3】:又一个 Java 实现。这是自上而下的 DP,也就是记忆。它还打印出除了最大产品之外的实际组件。
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");
奖励:任何有兴趣的人都可以进行暴力破解。不是特别聪明,但它使回溯步骤变得简单。
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");
【讨论】:
【参考方案4】:我在这里假设乘法运算符的所需数量 m 是问题的一部分,以及数字字符串 s。
您可以使用 tabular method(又称“动态编程”)和 O(m |s|2) 来解决这个问题O(|s|) 位长的数字的乘法。 optimal computational complexity of multiplication 是未知的,但使用教科书乘法算法,这是 O(m |s|4) 总体。
(这个想法是计算每个子问题的答案,每个子问题由字符串的尾部和一个数字m′ ≤ m组成。有 O( m |s|) 这样的子问题和解决每个子问题都涉及 O(|s|) 乘以 O(|s em>|) 位长。)
在 Python 中,您可以像这样使用 Python 装饰器库中的 @memoized
decorator 对其进行编程:
@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))
如果你习惯了动态编程的自下而上的形式,你建立一个表,这种自上而下的形式可能看起来很奇怪,但实际上@memoized
decorator在cache
属性中维护了表功能:
>>> max_product('56789', 1)
51102
>>> max_product.cache
('89', 0): 89, ('9', 0): 9, ('6789', 0): 6789, ('56789', 1): 51102, ('789', 0): 789
【讨论】:
不幸的是,我没有答案,但当时感觉就像是一个动态编程问题。不敢相信我在电话面试中被问到一个动态编程问题...... +1,但请注意 Python 中的字符串切片增加了额外的复杂性:每个切片在s
中花费线性时间。 (原则上可以避免,但代码不会那么优雅:)
@larsmans:切片的复杂度是 O(|s|),主要是乘法的复杂度(据我们所知)。
我不能肯定这是正确的,但我所知道的动态编程似乎可以计算出正确的答案。再次感谢!
@Dukeling,@memoized
会自动处理记忆(即您的A[position][count]
),因此您无需将其包含在 Python 代码中。不过,您需要在 Java 代码中执行此操作。【参考方案5】:
这是一个迭代动态规划解决方案。
相对于the recursive version(应该有类似的运行时间)。
基本思路:
A[position][count]
是使用count
乘法可以在位置position
处结束的最大数字。
所以:
A[position][count] = max(for i = 0 to position
A[i][count-1] * input.substring(i, position))
对每个位置和每个计数执行此操作,然后以所需的乘法次数将每个位置与整个剩余字符串相乘。
复杂性:
给定一个字符串|s|
,并插入m
乘法运算符...
O(m|s|<sup>2</sup>g(s))
其中g(s)
是the complexity of multiplication。
Java 代码:
static long solve(String digits, int multiplications)
if (multiplications == 0)
return Long.parseLong(digits);
// Preprocessing - set up substring values
long[][] substrings = new long[digits.length()][digits.length()+1];
for (int i = 0; i < digits.length(); i++)
for (int j = i+1; j <= digits.length(); j++)
substrings[i][j] = Long.parseLong(digits.substring(i, j));
// Calculate multiplications from the left
long[][] A = new long[digits.length()][multiplications+1];
A[0][0] = 1;
for (int i = 1; i < A.length; i++)
A[i][0] = substrings[0][i];
for (int j = 1; j < A[0].length; j++)
long max = -1;
for (int i2 = 0; i2 < i; i2++)
long l = substrings[i2][i];
long prod = l * A[i2][j-1];
max = Math.max(max, prod);
A[i][j] = max;
// Multiply left with right and find maximum
long max = -1;
for (int i = 1; i < A.length; i++)
max = Math.max(max, substrings[i][A.length] * A[i][multiplications]);
return max;
一个非常基本的测试:
System.out.println(solve("99287", 1));
System.out.println(solve("99287", 2));
System.out.println(solve("312", 1));
打印:
86304
72036
62
是的,它只打印最大值。如果需要,让它实际打印总和并不难。
【讨论】:
左乘右?左右分别指什么?为什么需要这样做? A[position][count] = max(for i = 0 to position A[i][count-1] * input.substring(i, position)) ... 这是在哪里实现的在你的代码中? 你能解释一下最后一个 for 循环在做什么吗?为什么从 i=1 开始? "A[position][count] 是使用 count 乘法可以在位置 position 结束时获得的最大数字。"这不可能是真的。否则,不会 A[size of digits string][# multiplications] 使用所有数字和所需的乘法数为您提供最高数字。基本上,在我看来,您对 A 的定义告诉我们如何获得问题的答案。但是然后你忽略它并在最后有一些最终循环?【参考方案6】:这是另一个 Java 解决方案。 (我知道“312”和 1 乘法是正确的,我认为它适用于其他人......
你得记住如何自己获得递归方法的复杂性,哈哈。
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));
【讨论】:
除了因为溢出导致 1826456903521651 的案例失败之外,这通过了我所有的测试案例。【参考方案7】:java 版本,虽然 Python 已经展示了它的功能优势并击败了我:
private static class Solution
BigInteger product;
String expression;
private static Solution solve(String digits, int multiplications)
if (digits.length() < multiplications + 1)
return null; // No solutions
if (multiplications == 0)
Solution solution = new Solution();
solution.product = new BigInteger(digits);
solution.expression = digits;
return solution;
// Position of first '*':
Solution max = null;
for (int i = 1; i < digits.length() - (multiplications - 1); ++i)
BigInteger n = new BigInteger(digits.substring(0, i));
Solution solutionRest = solve(digits.substring(i), multiplications - 1);
n = n.multiply(solutionRest.product);
if (max == null || n.compareTo(max.product) > 0)
solutionRest.product = n;
solutionRest.expression = digits.substring(0, i) + "*"
+ solutionRest.expression;
max = solutionRest;
return max;
private static void test(String digits, int multiplications)
Solution solution = solve(digits, multiplications);
System.out.printf("%s %d -> %s = %s%n", digits, multiplications,
solution.expression, solution.product.toString());
public static void main(String[] args)
test("1826456903521651", 5);
输出
1826456903521651 5 -> 182*645*6*903*521*651 = 215719207032420
【讨论】:
我认为这里 Python 的主要优点是你不必做这么多的打字!【参考方案8】:我很确定答案是简单地将*
s 放在最大数字之前,这样最大的数字就会产生最大的影响。例如,如果我们有
1826456903521651
我们有五个乘法,这就是答案。
1*82*645*6*903521*651
所以运行时间是线性的。
编辑:好的,这是错误的。我们有两个反例。
【讨论】:
这是一道数学题,我们都记得“相当肯定”不会得到认可 ;^) 在 n 位数字中找到 k 个最大位不是 O(n) - 这是最坏的情况O(n log n) 根据this standard reference @RoundTower。这不是真的,如果数字介于 0 和 9 之间,则尤其不正确。如果数字在 10 次时,您可以简单地遍历整个字符串以找到最大的 k 个数字。或者您可以使用订单统计查找算法。 作为忏悔我提供一个反例:9 * 9287 反例:在198
中放置一个*
。【参考方案9】:
想到了,这是受bars and stars问题影响的蛮力方法。
假设我们的号码是“12345”,我们需要使用 2 个 * 运算符。我们可以把字符串 12345 看成
1_2_3_4_5
我们可以将两个 * 运算符放在任何下划线上。由于有 4 个下划线和 2 个 * 运算符,因此有 4 种选择 2(或 6)种不同的方式来放置运算符。比较这 6 种可能性并抓住最大的数字。类似的方法可用于更大的字符串和更多的 * 运算符。
【讨论】:
不是downvoter,但这个答案并不是真正的'a'蛮力方法,它是蛮力方法 Gareth Rees 的动态规划方法需要多项式时间,而您的则需要阶乘时间,因此对于大输入而言,它是一个不那么无趣的解决方案。以上是关于给定一串数字和多个乘法运算符,可以计算出的最大数字是多少?的主要内容,如果未能解决你的问题,请参考以下文章