2232. Minimize Result by Adding Parentheses to Expression
Problem Description
You are given a string expression
in the format "<num1>+<num2>"
where <num1>
and <num2>
are positive integers.
Your task is to add exactly one pair of parentheses to the expression such that:
- The left parenthesis
(
must be placed somewhere to the left of the+
sign - The right parenthesis
)
must be placed somewhere to the right of the+
sign - After adding the parentheses, the expression remains mathematically valid
- The resulting expression evaluates to the smallest possible value
For example, if the expression is "247+38"
, you could place parentheses in various ways:
"2(47+3)8"
evaluates to2 * (47 + 3) * 8 = 2 * 50 * 8 = 800
"2(47+38)"
evaluates to2 * (47 + 38) = 2 * 85 = 170
"(247+3)8"
evaluates to(247 + 3) * 8 = 250 * 8 = 2000
"24(7+38)"
evaluates to24 * (7 + 38) = 24 * 45 = 1080
The goal is to find the placement that gives the minimum result. In this case, "2(47+38)"
with a value of 170 would be the answer.
Note that:
- Numbers outside the parentheses are multiplied with the sum inside the parentheses
- If there's no number before the left parenthesis, it's treated as multiplication by 1
- If there's no number after the right parenthesis, it's treated as multiplication by 1
Return the expression string with the parentheses added that produces the minimum value. If multiple placements yield the same minimum value, return any valid one.
Intuition
When we place parentheses in the expression, we're essentially deciding how to split the two numbers and what operations to perform. Let's think about what happens when we place parentheses at different positions.
Given an expression like "247+38"
, when we place parentheses like "24(7+38)"
, we're actually creating three parts:
- The part before the left parenthesis:
"24"
- The part inside the parentheses:
"7+38"
- The part after the right parenthesis:
""
(empty in this case)
The key insight is that the final value is calculated as: before × (inside_sum) × after
So for "24(7+38)"
, it becomes: 24 × (7 + 38) × 1 = 24 × 45 = 1080
To minimize the result, we need to consider all possible ways to split the original numbers. For the left number <num1>
, we can split it at any position (or not split at all). Similarly, for the right number <num2>
, we can split it at any position.
The brute force approach becomes clear: try all possible positions for the left parenthesis (which determines how we split <num1>
) and all possible positions for the right parenthesis (which determines how we split <num2>
). For each combination:
- Extract the part of
<num1>
that goes inside the parentheses - Extract the part of
<num2>
that goes inside the parentheses - Calculate the sum inside the parentheses
- Multiply by the parts outside (treating empty parts as 1)
- Keep track of the minimum value and its corresponding parenthesis placement
Since we're trying all possible combinations and the numbers are guaranteed to fit in a 32-bit integer, this exhaustive search will find the optimal solution.
Solution Approach
The implementation follows a nested loop approach to try all possible parenthesis placements:
-
Split the expression: First, we split the original expression by the
"+"
sign to get the left numberl
and right numberr
. We also store their lengths asm
andn
. -
Initialize tracking variables: We use
mi
to track the minimum value found (initialized to infinity) andans
to store the corresponding expression string. -
Try all possible splits: We use two nested loops:
- Outer loop with index
i
from0
tom-1
: determines where to place the left parenthesis in the left number - Inner loop with index
j
from0
ton-1
: determines where to place the right parenthesis in the right number
- Outer loop with index
-
For each combination
(i, j)
, we extract three components:c = int(l[i:]) + int(r[:j+1])
: The sum inside the parenthesesl[i:]
is the part of the left number inside parenthesesr[:j+1]
is the part of the right number inside parentheses
a
: The multiplier before the parentheses- If
i == 0
, there's nothing before, soa = 1
- Otherwise,
a = int(l[:i])
- If
b
: The multiplier after the parentheses- If
j == n-1
, there's nothing after, sob = 1
- Otherwise,
b = int(r[j+1:])
- If
-
Calculate the result: The total value is
t = a * b * c
-
Update minimum: If this value
t
is less than our current minimummi
, we update bothmi
and construct the answer string using string formatting:ans = f"{l[:i]}({l[i:]}+{r[:j+1]}){r[j+1:]}"
This places the parentheses at the correct positions in the original expression.
-
Return the result: After trying all combinations, return the
ans
string that corresponds to the minimum value.
The time complexity is O(m * n)
where m
and n
are the lengths of the two numbers, as we try all possible split positions. The space complexity is O(1)
aside from the output string.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with the example "12+34"
.
Step 1: Split the expression
- Split by
"+"
:l = "12"
,r = "34"
- Lengths:
m = 2
,n = 2
Step 2: Initialize tracking
mi = infinity
ans = ""
Step 3: Try all possible parenthesis placements
We'll iterate through all combinations of (i, j)
:
When i = 0, j = 0:
- Inside parentheses:
l[0:]
+r[:1]
="12"
+"3"
=12 + 3 = 15
- Before parentheses:
l[:0]
=""
→a = 1
- After parentheses:
r[1:]
="4"
→b = 4
- Result:
t = 1 × 15 × 4 = 60
- Expression:
"(12+3)4"
- Update:
mi = 60
,ans = "(12+3)4"
When i = 0, j = 1:
- Inside parentheses:
l[0:]
+r[:2]
="12"
+"34"
=12 + 34 = 46
- Before parentheses:
l[:0]
=""
→a = 1
- After parentheses:
r[2:]
=""
→b = 1
- Result:
t = 1 × 46 × 1 = 46
- Expression:
"(12+34)"
- Update:
mi = 46
,ans = "(12+34)"
When i = 1, j = 0:
- Inside parentheses:
l[1:]
+r[:1]
="2"
+"3"
=2 + 3 = 5
- Before parentheses:
l[:1]
="1"
→a = 1
- After parentheses:
r[1:]
="4"
→b = 4
- Result:
t = 1 × 5 × 4 = 20
- Expression:
"1(2+3)4"
- Update:
mi = 20
,ans = "1(2+3)4"
When i = 1, j = 1:
- Inside parentheses:
l[1:]
+r[:2]
="2"
+"34"
=2 + 34 = 36
- Before parentheses:
l[:1]
="1"
→a = 1
- After parentheses:
r[2:]
=""
→b = 1
- Result:
t = 1 × 36 × 1 = 36
- Expression:
"1(2+34)"
- No update since 36 > 20
Step 4: Return result
The minimum value is 20, achieved with the expression "1(2+3)4"
.
This demonstrates how the algorithm systematically checks all possible ways to place parentheses and finds the placement that yields the smallest result.
Solution Implementation
1class Solution:
2 def minimizeResult(self, expression: str) -> str:
3 # Split the expression into left and right operands around the '+' sign
4 left_operand, right_operand = expression.split("+")
5 left_length, right_length = len(left_operand), len(right_operand)
6
7 # Initialize minimum value and result string
8 min_value = float('inf')
9 result = None
10
11 # Try all possible positions for left parenthesis (before digit at index i in left_operand)
12 for left_paren_pos in range(left_length):
13 # Try all possible positions for right parenthesis (after digit at index j in right_operand)
14 for right_paren_pos in range(right_length):
15 # Calculate the sum inside parentheses
16 # left_operand[left_paren_pos:] + right_operand[:right_paren_pos + 1]
17 inside_sum = int(left_operand[left_paren_pos:]) + int(right_operand[:right_paren_pos + 1])
18
19 # Calculate the multiplier on the left of the parentheses
20 # If parenthesis is at the start, multiplier is 1
21 left_multiplier = 1 if left_paren_pos == 0 else int(left_operand[:left_paren_pos])
22
23 # Calculate the multiplier on the right of the parentheses
24 # If parenthesis is at the end, multiplier is 1
25 right_multiplier = 1 if right_paren_pos == right_length - 1 else int(right_operand[right_paren_pos + 1:])
26
27 # Calculate the total value: left_multiplier * inside_sum * right_multiplier
28 total_value = left_multiplier * inside_sum * right_multiplier
29
30 # Update minimum value and result string if we found a smaller value
31 if total_value < min_value:
32 min_value = total_value
33 # Construct the result string with parentheses at the current positions
34 result = f"{left_operand[:left_paren_pos]}({left_operand[left_paren_pos:]}+{right_operand[:right_paren_pos + 1]}){right_operand[right_paren_pos + 1:]}"
35
36 return result
37
1class Solution {
2 public String minimizeResult(String expression) {
3 // Find the position of the '+' operator in the expression
4 int plusIndex = expression.indexOf('+');
5
6 // Split the expression into left and right parts around the '+'
7 String leftPart = expression.substring(0, plusIndex);
8 String rightPart = expression.substring(plusIndex + 1);
9
10 // Get the lengths of both parts
11 int leftLength = leftPart.length();
12 int rightLength = rightPart.length();
13
14 // Initialize variables to track the minimum result and the answer string
15 int minimumValue = Integer.MAX_VALUE;
16 String resultExpression = "";
17
18 // Try all possible positions for the left parenthesis (before each digit in leftPart)
19 for (int leftParenPos = 0; leftParenPos < leftLength; ++leftParenPos) {
20 // Try all possible positions for the right parenthesis (after each digit in rightPart)
21 for (int rightParenPos = 0; rightParenPos < rightLength; ++rightParenPos) {
22 // Calculate the sum inside the parentheses
23 // leftPart.substring(leftParenPos) gets the part inside parentheses on the left
24 // rightPart.substring(0, rightParenPos + 1) gets the part inside parentheses on the right
25 int sumInsideParentheses = Integer.parseInt(leftPart.substring(leftParenPos)) +
26 Integer.parseInt(rightPart.substring(0, rightParenPos + 1));
27
28 // Calculate the multiplier from the left side (outside parentheses)
29 // If parenthesis is at the start, multiplier is 1
30 int leftMultiplier = (leftParenPos == 0) ? 1 :
31 Integer.parseInt(leftPart.substring(0, leftParenPos));
32
33 // Calculate the multiplier from the right side (outside parentheses)
34 // If parenthesis is at the end, multiplier is 1
35 int rightMultiplier = (rightParenPos == rightLength - 1) ? 1 :
36 Integer.parseInt(rightPart.substring(rightParenPos + 1));
37
38 // Calculate the total result: left * (sum) * right
39 int currentResult = leftMultiplier * sumInsideParentheses * rightMultiplier;
40
41 // Update minimum value and result expression if we found a better solution
42 if (currentResult < minimumValue) {
43 minimumValue = currentResult;
44 // Format the expression with parentheses at the current positions
45 resultExpression = String.format("%s(%s+%s)%s",
46 leftPart.substring(0, leftParenPos), // part before left parenthesis
47 leftPart.substring(leftParenPos), // part inside parentheses (left)
48 rightPart.substring(0, rightParenPos + 1), // part inside parentheses (right)
49 rightPart.substring(rightParenPos + 1)); // part after right parenthesis
50 }
51 }
52 }
53
54 return resultExpression;
55 }
56}
57
1#include <string>
2#include <vector>
3#include <climits>
4#include <algorithm>
5
6class Solution {
7public:
8 /**
9 * Minimizes the result of an expression by adding parentheses around the addition operation.
10 * The goal is to find the optimal placement of parentheses to minimize the evaluated result.
11 * @param expression - A string containing two numbers separated by a '+' sign
12 * @return The expression with parentheses placed to minimize the result
13 */
14 string minimizeResult(string expression) {
15 // Find the position of '+' sign to split the expression
16 int plusPos = expression.find('+');
17
18 // Split the expression into two numbers at the '+' sign
19 string leftNumber = expression.substr(0, plusPos);
20 string rightNumber = expression.substr(plusPos + 1);
21
22 // Initialize variables to track the minimum sum and result
23 int minimumValue = INT_MAX;
24 string resultExpression = "";
25
26 // Iterate through all possible positions for the left parenthesis
27 // i represents the number of digits before the left parenthesis
28 for (int i = 0; i < leftNumber.length(); i++) {
29 // Iterate through all possible positions for the right parenthesis
30 // j represents the number of digits inside parentheses on the right side
31 for (int j = 1; j <= rightNumber.length(); j++) {
32 // Extract the parts of the expression
33 string leftOuter = leftNumber.substr(0, i);
34 string leftInner = leftNumber.substr(i);
35 string rightInner = rightNumber.substr(0, j);
36 string rightOuter = rightNumber.substr(j);
37
38 // Calculate the result: (inner sum) * left outer * right outer
39 int leftOuterValue = getNumericValue(leftOuter);
40 int leftInnerValue = getNumericValue(leftInner);
41 int rightInnerValue = getNumericValue(rightInner);
42 int rightOuterValue = getNumericValue(rightOuter);
43
44 int currentValue = (leftInnerValue + rightInnerValue) * leftOuterValue * rightOuterValue;
45
46 // Update minimum value and result expression if current is smaller
47 if (currentValue < minimumValue) {
48 minimumValue = currentValue;
49 resultExpression = leftOuter + "(" + leftInner + "+" + rightInner + ")" + rightOuter;
50 }
51 }
52 }
53
54 return resultExpression;
55 }
56
57private:
58 /**
59 * Converts a string of digits to a number.
60 * Returns 1 if the string is empty (for multiplication purposes).
61 * @param str - String containing digits
62 * @return The numeric value of the string or 1 if empty
63 */
64 int getNumericValue(const string& str) {
65 // Return 1 for empty strings to maintain multiplication identity
66 if (str.empty()) {
67 return 1;
68 }
69 // Convert string to integer
70 return stoi(str);
71 }
72};
73
1/**
2 * Minimizes the result of an expression by adding parentheses around the addition operation.
3 * The goal is to find the optimal placement of parentheses to minimize the evaluated result.
4 * @param expression - A string containing two numbers separated by a '+' sign
5 * @returns The expression with parentheses placed to minimize the result
6 */
7function minimizeResult(expression: string): string {
8 // Split the expression into two numbers at the '+' sign
9 const [leftNumber, rightNumber] = expression.split('+');
10
11 // Initialize variables to track the minimum sum and result
12 let minimumValue = Number.MAX_SAFE_INTEGER;
13 let resultExpression = '';
14
15 // Initialize arrays to manage digit partitioning
16 let leftOuterDigits: string[] = [];
17 let leftInnerDigits: string[] = leftNumber.split('');
18 let rightInnerDigits: string[] = rightNumber.split('');
19 let rightOuterDigits: string[] = [];
20
21 // Iterate through all possible positions for the left parenthesis
22 while (leftInnerDigits.length) {
23 // Reset right side arrays for each left parenthesis position
24 rightInnerDigits = rightNumber.split('');
25 rightOuterDigits = [];
26
27 // Iterate through all possible positions for the right parenthesis
28 while (rightInnerDigits.length) {
29 // Calculate the result: (inner sum) * left outer * right outer
30 const currentValue = (getNum(leftInnerDigits) + getNum(rightInnerDigits))
31 * getNum(leftOuterDigits)
32 * getNum(rightOuterDigits);
33
34 // Update minimum value and result expression if current is smaller
35 if (currentValue < minimumValue) {
36 minimumValue = currentValue;
37 resultExpression = `${leftOuterDigits.join('')}(${leftInnerDigits.join('')}+${rightInnerDigits.join('')})${rightOuterDigits.join('')}`;
38 }
39
40 // Move one digit from right inner to right outer
41 rightOuterDigits.unshift(rightInnerDigits.pop()!);
42 }
43
44 // Move one digit from left inner to left outer
45 leftOuterDigits.push(leftInnerDigits.shift()!);
46 }
47
48 return resultExpression;
49}
50
51/**
52 * Converts an array of string digits to a number.
53 * Returns 1 if the array is empty (for multiplication purposes).
54 * @param digitArray - Array of string digits
55 * @returns The numeric value of the joined digits or 1 if empty
56 */
57function getNum(digitArray: string[]): number {
58 return digitArray.length ? Number(digitArray.join('')) : 1;
59}
60
Time and Space Complexity
Time Complexity: O(m * n)
where m
is the length of the left operand and n
is the length of the right operand in the expression.
The algorithm uses two nested loops:
- The outer loop iterates through all possible positions
i
in the left operand (from0
tom-1
) - The inner loop iterates through all possible positions
j
in the right operand (from0
ton-1
)
For each combination of (i, j)
, the operations performed are:
- String slicing operations:
l[i:]
,r[:j+1]
,l[:i]
,r[j+1:]
- each takesO(1)
toO(m)
orO(n)
time - Integer conversions:
int()
operations -O(k)
wherek
is the length of the substring - Arithmetic operations and comparisons -
O(1)
- String formatting -
O(m + n)
Since string operations are bounded by the length of the operands and we perform them a constant number of times per iteration, the dominant factor is the nested loops, giving us O(m * n)
.
Space Complexity: O(m + n)
The space usage includes:
- Variables
l
andr
to store the split operands -O(m + n)
- Temporary strings created during slicing operations -
O(m + n)
in the worst case - Variable
ans
to store the result string -O(m + n)
- Other scalar variables (
i
,j
,a
,b
,c
,mi
,t
) -O(1)
The space complexity is dominated by the string storage, resulting in O(m + n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Empty String Multiplication
One of the most common pitfalls is not handling empty strings correctly when extracting multipliers. When the parenthesis is placed at the beginning or end of the expression, the substring extraction might result in an empty string, which cannot be converted to an integer.
Problem Example:
# If left_paren_pos = 0, then left_operand[:0] = ""
# int("") will throw a ValueError
left_multiplier = int(left_operand[:left_paren_pos]) # Error!
Solution: Always check if the substring is empty before converting to integer, or use conditional logic:
left_multiplier = 1 if left_paren_pos == 0 else int(left_operand[:left_paren_pos])
right_multiplier = 1 if right_paren_pos == right_length - 1 else int(right_operand[right_paren_pos + 1:])
2. Incorrect Index Boundaries
Another pitfall is using incorrect indices for string slicing, especially for the right parenthesis position. The confusion often arises from whether to use right_paren_pos
or right_paren_pos + 1
.
Problem Example:
# Incorrect: missing the digit at right_paren_pos
inside_sum = int(left_operand[left_paren_pos:]) + int(right_operand[:right_paren_pos])
Solution:
Remember that Python slicing is exclusive of the end index, so use right_paren_pos + 1
:
inside_sum = int(left_operand[left_paren_pos:]) + int(right_operand[:right_paren_pos + 1])
3. Off-by-One Errors in Loop Ranges
Using incorrect loop ranges can cause you to miss valid parenthesis placements or attempt invalid ones.
Problem Example:
# This misses the case where all digits are inside parentheses
for left_paren_pos in range(1, left_length): # Starts from 1 instead of 0
for right_paren_pos in range(right_length - 1): # Excludes last position
Solution: Ensure loops cover all valid positions:
for left_paren_pos in range(left_length): # 0 to left_length-1
for right_paren_pos in range(right_length): # 0 to right_length-1
4. String Construction Errors
When building the result string, it's easy to misplace parts of the original expression or forget to include all components.
Problem Example:
# Forgetting parts of the expression or using wrong indices
result = f"({left_operand[left_paren_pos:]}+{right_operand[:right_paren_pos + 1]})"
# Missing: left_operand[:left_paren_pos] at the beginning and right_operand[right_paren_pos + 1:] at the end
Solution: Carefully construct the string with all four parts:
result = f"{left_operand[:left_paren_pos]}({left_operand[left_paren_pos:]}+{right_operand[:right_paren_pos + 1]}){right_operand[right_paren_pos + 1:]}"
The four parts are:
- Numbers before the left parenthesis
- Opening parenthesis and numbers from left operand inside
- Plus sign and numbers from right operand inside with closing parenthesis
- Numbers after the right parenthesis
Which technique can we use to find the middle of a linked list?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!