43. Multiply Strings
Problem Description
You are given two non-negative integers represented as strings, num1
and num2
. Your task is to multiply these two numbers and return their product as a string.
The key constraint is that you cannot use any built-in BigInteger library or convert the strings directly to integers. This means you need to implement the multiplication algorithm manually, working with the string representations digit by digit.
For example:
- If
num1 = "2"
andnum2 = "3"
, the output should be"6"
- If
num1 = "123"
andnum2 = "456"
, the output should be"56088"
The solution simulates the traditional grade-school multiplication method. When multiplying two numbers with m
and n
digits respectively, the product will have at most m + n
digits. The algorithm:
- Creates an array of size
m + n
to store intermediate results - Multiplies each digit of
num1
with each digit ofnum2
, placing the result at the appropriate position in the array - Handles carry-over operations by iterating through the array from right to left
- Converts the final array back to a string, removing any leading zeros
The key insight is that when multiplying digit at position i
in num1
with digit at position j
in num2
, the result contributes to position i + j + 1
in the result array (using 0-based indexing from the left).
Intuition
Think about how you learned to multiply large numbers by hand in elementary school. When you multiply 123 × 456
, you would:
- Multiply
123
by6
, getting738
- Multiply
123
by5
, getting615
, but shift it one position left (add a zero) - Multiply
123
by4
, getting492
, but shift it two positions left (add two zeros) - Add all these intermediate results together
The key observation is that each digit multiplication contributes to a specific position in the final result. When we multiply the digit at position i
from the first number with the digit at position j
from the second number, where do we place this result?
Consider multiplying num1[i] × num2[j]
:
num1[i]
represents a digit at the(m-1-i)
-th power of 10 from the rightnum2[j]
represents a digit at the(n-1-j)
-th power of 10 from the right- Their product contributes to the
(m-1-i) + (n-1-j) = (m+n-2-i-j)
-th power of 10 from the right
In array terms (0-indexed from left), this maps to position i + j + 1
in our result array.
Why do we need an array of size m + n
? The smallest product would be 10^(m-1) × 10^(n-1) = 10^(m+n-2)
(like 100 × 100 = 10000
), which has m + n - 1
digits. The largest would be (10^m - 1) × (10^n - 1)
(like 999 × 999 = 998001
), which could have m + n
digits. So m + n
is the maximum possible length.
By processing from right to left (least significant to most significant digit) and handling carries naturally through integer division and modulo operations, we simulate the exact process of manual multiplication while avoiding integer overflow issues that would occur if we tried to convert the entire strings to numbers.
Learn more about Math patterns.
Solution Approach
The implementation follows the mathematical multiplication simulation approach. Let's walk through the algorithm step by step:
1. Handle Edge Cases
if num1 == "0" or num2 == "0": return "0"
If either number is zero, the product is zero. This early return optimizes the edge case.
2. Initialize Result Array
m, n = len(num1), len(num2)
arr = [0] * (m + n)
We create an array of size m + n
to store the intermediate multiplication results. This array will hold each digit of the final product.
3. Perform Digit-by-Digit Multiplication
for i in range(m - 1, -1, -1):
a = int(num1[i])
for j in range(n - 1, -1, -1):
b = int(num2[j])
arr[i + j + 1] += a * b
We iterate through both numbers from right to left (least significant to most significant digit). For each pair of digits:
- Convert the character digits to integers
- Multiply them together
- Add the result to position
i + j + 1
in the array
The key insight is the position formula: when multiplying digit at index i
in num1
with digit at index j
in num2
, the result goes to position i + j + 1
. This is because:
- If
num1
hasm
digits andnum2
hasn
digits - The digit at position
i
represents10^(m-1-i)
- The digit at position
j
represents10^(n-1-j)
- Their product contributes to
10^(m-1-i+n-1-j) = 10^(m+n-2-i-j)
- In our 0-indexed array from the left, this maps to position
i + j + 1
4. Handle Carries
for i in range(m + n - 1, 0, -1):
arr[i - 1] += arr[i] // 10
arr[i] %= 10
After all multiplications, some positions might have values greater than 9. We process the array from right to left:
- The carry (
arr[i] // 10
) is added to the previous position - The current position keeps only the ones digit (
arr[i] % 10
)
5. Convert to String and Remove Leading Zeros
i = 0 if arr[0] else 1
return "".join(str(x) for x in arr[i:])
Finally, we convert the array to a string. If the first element is 0 (which happens when the product doesn't need all m + n
digits), we start from index 1 to avoid leading zeros.
The time complexity is O(m × n)
as we multiply each digit of num1
with each digit of num2
. The space complexity is O(m + n)
for storing the result array.
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 multiplying num1 = "123"
and num2 = "45"
step by step.
Step 1: Initialize
m = 3
(length of "123"),n = 2
(length of "45")- Create result array:
arr = [0, 0, 0, 0, 0]
(size m + n = 5)
Step 2: Digit-by-Digit Multiplication
We'll multiply each digit, starting from the rightmost digits:
Round 1: i = 2 (digit '3' from "123")
- j = 1: Multiply 3 × 5 = 15, add to arr[2+1+1] = arr[4]
arr = [0, 0, 0, 0, 15]
- j = 0: Multiply 3 × 4 = 12, add to arr[2+0+1] = arr[3]
arr = [0, 0, 0, 12, 15]
Round 2: i = 1 (digit '2' from "123")
- j = 1: Multiply 2 × 5 = 10, add to arr[1+1+1] = arr[3]
arr = [0, 0, 0, 22, 15]
(12 + 10 = 22)
- j = 0: Multiply 2 × 4 = 8, add to arr[1+0+1] = arr[2]
arr = [0, 0, 8, 22, 15]
Round 3: i = 0 (digit '1' from "123")
- j = 1: Multiply 1 × 5 = 5, add to arr[0+1+1] = arr[2]
arr = [0, 0, 13, 22, 15]
(8 + 5 = 13)
- j = 0: Multiply 1 × 4 = 4, add to arr[0+0+1] = arr[1]
arr = [0, 4, 13, 22, 15]
Step 3: Handle Carries
Process from right to left to propagate carries:
-
Position 4:
arr[4] = 15
- Carry: 15 ÷ 10 = 1 → add to arr[3]
- Keep: 15 % 10 = 5
arr = [0, 4, 13, 23, 5]
-
Position 3:
arr[3] = 23
- Carry: 23 ÷ 10 = 2 → add to arr[2]
- Keep: 23 % 10 = 3
arr = [0, 4, 15, 3, 5]
-
Position 2:
arr[2] = 15
- Carry: 15 ÷ 10 = 1 → add to arr[1]
- Keep: 15 % 10 = 5
arr = [0, 5, 5, 3, 5]
-
Position 1:
arr[1] = 5
- Carry: 5 ÷ 10 = 0 → add to arr[0]
- Keep: 5 % 10 = 5
arr = [0, 5, 5, 3, 5]
Step 4: Convert to String
- First element is 0, so start from index 1
- Join arr[1:] = [5, 5, 3, 5] → "5535"
Verification: 123 × 45 = 5535 ✓
The key insight is how the position formula i + j + 1
correctly places each partial product. For instance, when we multiply the ones digit of "123" (position 2) with the tens digit of "45" (position 0), we get 3 × 4 = 12, which contributes to the tens place of the result (position 3 in our array).
Solution Implementation
1class Solution:
2 def multiply(self, num1: str, num2: str) -> str:
3 # Handle edge case where either number is zero
4 if num1 == "0" or num2 == "0":
5 return "0"
6
7 # Get lengths of both numbers
8 len1, len2 = len(num1), len(num2)
9
10 # Initialize result array with zeros
11 # Maximum possible length of product is len1 + len2
12 result = [0] * (len1 + len2)
13
14 # Multiply each digit of num1 with each digit of num2
15 # Starting from the rightmost digits (least significant)
16 for i in range(len1 - 1, -1, -1):
17 digit1 = int(num1[i])
18
19 for j in range(len2 - 1, -1, -1):
20 digit2 = int(num2[j])
21
22 # Multiply current digits and add to corresponding position
23 # Position i + j + 1 corresponds to the current digit position
24 result[i + j + 1] += digit1 * digit2
25
26 # Handle carries from right to left
27 for i in range(len1 + len2 - 1, 0, -1):
28 # Add carry to the previous position
29 result[i - 1] += result[i] // 10
30 # Keep only the single digit at current position
31 result[i] %= 10
32
33 # Find the starting index (skip leading zero if present)
34 start_index = 0 if result[0] != 0 else 1
35
36 # Convert result array to string and return
37 return "".join(str(digit) for digit in result[start_index:])
38
1class Solution {
2 public String multiply(String num1, String num2) {
3 // Handle edge case: if either number is "0", product is "0"
4 if ("0".equals(num1) || "0".equals(num2)) {
5 return "0";
6 }
7
8 // Get lengths of both input strings
9 int length1 = num1.length();
10 int length2 = num2.length();
11
12 // Initialize result array with maximum possible length
13 // Product of m-digit and n-digit numbers has at most (m + n) digits
14 int[] productArray = new int[length1 + length2];
15
16 // Perform multiplication digit by digit from right to left
17 for (int i = length1 - 1; i >= 0; i--) {
18 // Extract digit from num1
19 int digit1 = num1.charAt(i) - '0';
20
21 for (int j = length2 - 1; j >= 0; j--) {
22 // Extract digit from num2
23 int digit2 = num2.charAt(j) - '0';
24
25 // Multiply digits and add to corresponding position
26 // Position (i + j + 1) stores the product of digits at positions i and j
27 productArray[i + j + 1] += digit1 * digit2;
28 }
29 }
30
31 // Handle carries from right to left
32 for (int i = productArray.length - 1; i > 0; i--) {
33 // Add carry to the previous position
34 productArray[i - 1] += productArray[i] / 10;
35 // Keep only the single digit at current position
36 productArray[i] %= 10;
37 }
38
39 // Skip leading zero if present (product might have one less digit than maximum)
40 int startIndex = productArray[0] == 0 ? 1 : 0;
41
42 // Build the result string
43 StringBuilder result = new StringBuilder();
44 for (int i = startIndex; i < productArray.length; i++) {
45 result.append(productArray[i]);
46 }
47
48 return result.toString();
49 }
50}
51
1class Solution {
2public:
3 string multiply(string num1, string num2) {
4 // Handle edge case where either number is zero
5 if (num1 == "0" || num2 == "0") {
6 return "0";
7 }
8
9 // Get the lengths of both input strings
10 int len1 = num1.size();
11 int len2 = num2.size();
12
13 // Initialize result array with size len1 + len2 (maximum possible digits in product)
14 vector<int> result(len1 + len2, 0);
15
16 // Multiply each digit of num1 with each digit of num2
17 // Starting from the rightmost digit (least significant)
18 for (int i = len1 - 1; i >= 0; --i) {
19 int digit1 = num1[i] - '0'; // Convert char to int
20
21 for (int j = len2 - 1; j >= 0; --j) {
22 int digit2 = num2[j] - '0'; // Convert char to int
23
24 // Multiply digits and add to corresponding position
25 // Position i + j + 1 corresponds to the result position
26 result[i + j + 1] += digit1 * digit2;
27 }
28 }
29
30 // Handle carries from right to left
31 for (int i = result.size() - 1; i > 0; --i) {
32 result[i - 1] += result[i] / 10; // Add carry to previous position
33 result[i] %= 10; // Keep only the last digit
34 }
35
36 // Skip leading zeros (if any)
37 int startIndex = result[0] == 0 ? 1 : 0;
38
39 // Build the final result string
40 string answer;
41 for (int i = startIndex; i < result.size(); ++i) {
42 answer += '0' + result[i]; // Convert int to char and append
43 }
44
45 return answer;
46 }
47};
48
1/**
2 * Multiplies two non-negative integers represented as strings
3 * @param num1 - First number as string
4 * @param num2 - Second number as string
5 * @returns Product of num1 and num2 as string
6 */
7function multiply(num1: string, num2: string): string {
8 // Early return for zero multiplication
9 if (num1 === '0' || num2 === '0') {
10 return '0';
11 }
12
13 // Get lengths of input numbers
14 const length1: number = num1.length;
15 const length2: number = num2.length;
16
17 // Initialize result array with maximum possible length
18 // Product of m-digit and n-digit numbers has at most m+n digits
19 const resultArray: number[] = Array(length1 + length2).fill(0);
20
21 // Perform digit-by-digit multiplication from right to left
22 for (let i: number = length1 - 1; i >= 0; i--) {
23 const digit1: number = +num1[i];
24
25 for (let j: number = length2 - 1; j >= 0; j--) {
26 const digit2: number = +num2[j];
27
28 // Multiply current digits and add to corresponding position
29 // Position i+j+1 stores the product of digits at positions i and j
30 resultArray[i + j + 1] += digit1 * digit2;
31 }
32 }
33
34 // Handle carry propagation from right to left
35 for (let i: number = resultArray.length - 1; i > 0; i--) {
36 // Add carry to previous position
37 resultArray[i - 1] += Math.floor(resultArray[i] / 10);
38 // Keep only the last digit at current position
39 resultArray[i] %= 10;
40 }
41
42 // Skip leading zeros in the result
43 let startIndex: number = 0;
44 while (startIndex < resultArray.length && resultArray[startIndex] === 0) {
45 startIndex++;
46 }
47
48 // Convert result array to string and return
49 return resultArray.slice(startIndex).join('');
50}
51
Time and Space Complexity
The time complexity is O(m × n)
, where m
is the length of num1
and n
is the length of num2
. This is because the algorithm uses nested loops where the outer loop iterates through all m
digits of num1
(from index m-1
to 0
), and for each digit, the inner loop iterates through all n
digits of num2
(from index n-1
to 0
). Each iteration performs a constant-time operation of multiplying two single digits and adding to the result array, resulting in m × n
total operations.
The space complexity is O(m + n)
. The algorithm creates an array arr
of size m + n
to store the intermediate and final results of the multiplication. This array size is determined by the maximum possible length of the product of two numbers, which is at most m + n
digits (for example, 99 × 99 = 9801
, where two 2-digit numbers produce a 4-digit result). The final string conversion also takes O(m + n)
space, but this doesn't change the overall space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Position Mapping When Placing Multiplication Results
One of the most common mistakes is incorrectly calculating where to place the product of two digits in the result array. Many developers mistakenly use arr[i + j]
instead of arr[i + j + 1]
.
Why this happens: The confusion arises from mixing up 0-based indexing with the mathematical significance of digit positions.
Incorrect approach:
# Wrong: This will place results in wrong positions
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
arr[i + j] += int(num1[i]) * int(num2[j]) # Wrong position!
Correct approach:
# Correct: Use i + j + 1 for proper position mapping
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
arr[i + j + 1] += int(num1[i]) * int(num2[j])
2. Forgetting to Handle Multiple Leading Zeros
While the provided solution handles a single leading zero, a more robust approach should handle edge cases where the result might have multiple leading zeros (though mathematically this shouldn't happen with valid inputs).
Potential issue:
# This only checks if the first element is 0 start_index = 0 if result[0] != 0 else 1
More robust solution:
# Find the first non-zero digit
start_index = 0
while start_index < len(result) - 1 and result[start_index] == 0:
start_index += 1
3. Processing Carries in the Wrong Direction
Another common mistake is processing carries from left to right instead of right to left, which can lead to incorrect propagation of carry values.
Wrong direction:
# Incorrect: Processing from left to right
for i in range(1, m + n):
arr[i] += arr[i - 1] // 10 # This won't work correctly
arr[i - 1] %= 10
Correct direction:
# Correct: Processing from right to left
for i in range(m + n - 1, 0, -1):
arr[i - 1] += arr[i] // 10
arr[i] %= 10
4. Not Handling the Edge Case of "0" Properly
Failing to check for zero inputs at the beginning can lead to unnecessary computation and potential issues with the final string conversion.
Issue without zero check:
def multiply(self, num1: str, num2: str) -> str:
# Without checking for "0", we might get "00" or "000" as output
m, n = len(num1), len(num2)
result = [0] * (m + n)
# ... multiplication logic ...
# This could return "00" for "0" × "10"
Solution: Always check for zero inputs at the beginning:
if num1 == "0" or num2 == "0": return "0"
5. Integer Overflow in Other Languages
While Python handles arbitrary precision integers automatically, in languages like Java or C++, storing a * b
directly might cause overflow for large digits multiplication plus existing value.
Potential overflow in other languages:
// In C++, this might overflow for large accumulated values result[i + j + 1] += digit1 * digit2;
Safe approach for other languages:
// Break down the operation to avoid overflow int product = digit1 * digit2; int sum = result[i + j + 1] + product; result[i + j + 1] = sum % 10; result[i + j] += sum / 10;
Which of the following is a good use case for backtracking?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
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
Want a Structured Path to Master System Design Too? Don’t Miss This!