Multiply Strings
Description
Given two non-negative integers num1 and num2 represented as strings, return their product, also represented as a string.
You are not allowed to use any built-in big-integer library or convert the input strings directly to integers.
In simpler terms: Imagine you are given two very large numbers written on paper — so large that they don't fit into a standard integer variable. Your job is to multiply them together the same way you would by hand on paper, digit by digit, and produce the result as a string of digits.
Examples
Example 1
Input: num1 = "2", num2 = "3"
Output: "6"
Explanation: The product of 2 and 3 is 6. Since both inputs are single-digit numbers, the multiplication is straightforward.
Example 2
Input: num1 = "123", num2 = "456"
Output: "56088"
Explanation: Multiplying 123 × 456 gives 56088. This is computed by multiplying each digit of one number by each digit of the other and summing partial products with proper positional shifts — exactly like long multiplication on paper.
Example 3
Input: num1 = "0", num2 = "52"
Output: "0"
Explanation: Any number multiplied by zero is zero. This is an important edge case to handle — the result should be "0", not "00" or an empty string.
Constraints
- 1 ≤ num1.length, num2.length ≤ 200
num1andnum2consist of digits only- Both
num1andnum2do not contain any leading zero, except the number 0 itself
Editorial
Brute Force
Intuition
The most natural way to think about multiplying two numbers is how we learned it in elementary school: repeated addition. Multiplication is simply adding one number to itself repeatedly.
For example, 123 × 4 means adding 123 four times: 123 + 123 + 123 + 123 = 492.
We can extend this to multi-digit multipliers by breaking them down: 123 × 456 = 123 × 400 + 123 × 50 + 123 × 6. But even the simpler interpretation — add num1 to itself num2 times — captures the brute force idea.
Since we cannot convert strings to integers directly, we need a helper function that adds two number-strings together. Then we loop num2 times, adding num1 to a running total each iteration.
The problem with this approach is clear: if num2 is a 200-digit number, we'd need to perform an astronomically large number of additions — far too many to ever complete.
Step-by-Step Explanation
Let's trace through with num1 = "12", num2 = "3":
Step 1: Initialize result = "0".
Step 2: Iteration 1: result = addStrings("0", "12") = "12".
Step 3: Iteration 2: result = addStrings("12", "12") = "24".
Step 4: Iteration 3: result = addStrings("24", "12") = "36".
Step 5: num2 exhausted (3 iterations). Return "36".
This works for small numbers, but for num2 = "999..." (200 digits), we'd need ~10^200 iterations — completely infeasible.
Algorithm
- Implement a helper function
addStrings(a, b)that adds two number-strings digit by digit with carry. - Initialize
result = "0". - Convert
num2to an iteration count (conceptually — since we can't convert to int for large numbers, this approach is inherently limited). - Loop
num2times:result = addStrings(result, num1). - Return
result.
Code
#include <string>
#include <algorithm>
using namespace std;
class Solution {
public:
string addStrings(string a, string b) {
string result;
int carry = 0;
int i = a.size() - 1, j = b.size() - 1;
while (i >= 0 || j >= 0 || carry) {
int sum = carry;
if (i >= 0) sum += a[i--] - '0';
if (j >= 0) sum += b[j--] - '0';
result += (char)('0' + sum % 10);
carry = sum / 10;
}
reverse(result.begin(), result.end());
return result;
}
string multiply(string num1, string num2) {
if (num1 == "0" || num2 == "0") return "0";
// This approach only works for small num2 values
// For demonstration, we convert num2 to int (violates constraint for large inputs)
string result = "0";
int times = stoi(num2); // Only works for small num2
for (int i = 0; i < times; i++) {
result = addStrings(result, num1);
}
return result;
}
};class Solution:
def multiply(self, num1: str, num2: str) -> str:
if num1 == "0" or num2 == "0":
return "0"
def add_strings(a: str, b: str) -> str:
result = []
carry = 0
i, j = len(a) - 1, len(b) - 1
while i >= 0 or j >= 0 or carry:
total = carry
if i >= 0:
total += int(a[i])
i -= 1
if j >= 0:
total += int(b[j])
j -= 1
result.append(str(total % 10))
carry = total // 10
return ''.join(reversed(result))
# Only works for small num2 values
result = "0"
times = int(num2) # Violates constraint for large inputs
for _ in range(times):
result = add_strings(result, num1)
return resultclass Solution {
private String addStrings(String a, String b) {
StringBuilder result = new StringBuilder();
int carry = 0;
int i = a.length() - 1, j = b.length() - 1;
while (i >= 0 || j >= 0 || carry > 0) {
int sum = carry;
if (i >= 0) sum += a.charAt(i--) - '0';
if (j >= 0) sum += b.charAt(j--) - '0';
result.append((char) ('0' + sum % 10));
carry = sum / 10;
}
return result.reverse().toString();
}
public String multiply(String num1, String num2) {
if (num1.equals("0") || num2.equals("0")) return "0";
// Only works for small num2 values
String result = "0";
int times = Integer.parseInt(num2); // Violates constraint for large inputs
for (int i = 0; i < times; i++) {
result = addStrings(result, num1);
}
return result;
}
}Complexity Analysis
Time Complexity: O(num2 × m)
Where num2 is the numeric value of the second string and m is the length of num1. We perform num2 string additions, each of which takes O(m) time for digit-by-digit addition. If num2 is a 200-digit number, num2 can be up to 10^200 — making this approach completely infeasible.
Space Complexity: O(m + n)
The result string can be at most m + n digits long. Each addition also uses O(m) temporary space.
Why This Approach Is Not Efficient
The repeated addition approach has a fatal flaw: the number of iterations depends on the numeric value of num2, not its length. With num2 up to 200 digits long, its numeric value can be as large as 10^200 — a number with 200 zeros. Even at a billion operations per second, this would take longer than the age of the universe.
The fundamental problem is that we're treating multiplication as iterated addition, which doesn't scale. We need an approach where the work depends on the number of digits (at most 200), not the numeric magnitude (up to 10^200).
The key insight: recall how you multiply on paper. You don't add 456 times. Instead, you multiply each digit of the bottom number by each digit of the top number, placing partial results at the correct position. This digit-by-digit approach does at most m × n single-digit multiplications — at most 200 × 200 = 40,000 operations. That's the approach we need.
Optimal Approach - Grade School Multiplication (Digit-by-Digit)
Intuition
Think back to how you multiply numbers by hand on paper. To compute 123 × 456, you would:
- Multiply 123 by 6 (ones digit of 456) → 738, write it starting at position 0.
- Multiply 123 by 5 (tens digit of 456) → 615, write it starting at position 1 (shifted left by one).
- Multiply 123 by 4 (hundreds digit of 456) → 492, write it starting at position 2 (shifted left by two).
- Add all three partial products together.
The crucial observation is: when you multiply digit num1[i] by digit num2[j], the result contributes to positions i + j and i + j + 1 in the final answer (counting positions from the most significant end).
More precisely, if we index both strings from the right (least significant digit), then the product of num1[i] and num2[j] lands at position i + j in a result array. We accumulate all such products, handle carries, and convert back to a string.
This is exactly what a result array of length m + n captures: the maximum possible length of the product of an m-digit number and an n-digit number is m + n digits (for example, 99 × 99 = 9801, which is 4 digits = 2 + 2).
Step-by-Step Explanation
Let's trace through with num1 = "12", num2 = "34" using the standard positional algorithm.
Result array has length m + n = 2 + 2 = 4, initialized to all zeros: [0, 0, 0, 0]. Index 0 is the most significant position.
We iterate i from right to left through num1, and for each i, iterate j from right to left through num2. For each pair, p1 = i+j (carry position), p2 = i+j+1 (ones position).
Step 1: i=1 (digit '2'), j=1 (digit '4'). p1=2, p2=3. mul=2×4=8. sum=8+result[3]=8. result[3]=8, carry=0. Result: [0,0,0,8].
Step 2: i=1 (digit '2'), j=0 (digit '3'). p1=1, p2=2. mul=2×3=6. sum=6+result[2]=6. result[2]=6, carry=0. Result: [0,0,6,8].
Step 3: i=0 (digit '1'), j=1 (digit '4'). p1=1, p2=2. mul=1×4=4. sum=4+result[2]=4+6=10. result[2]=10%10=0, carry=10/10=1 → result[1]+=1. Result: [0,1,0,8].
Step 4: i=0 (digit '1'), j=0 (digit '3'). p1=0, p2=1. mul=1×3=3. sum=3+result[1]=3+1=4. result[1]=4, carry=0. Result: [0,4,0,8].
Step 5: Convert result array [0,4,0,8] to string, skip leading zero → "408".
Result: 12 × 34 = 408 ✓. We used only 4 single-digit multiplications — O(m×n).
Grade School Multiplication — 12 × 34 — Watch how each digit-by-digit multiplication places its result at the correct position in the product array, with carries propagating naturally.
Algorithm
- If either
num1ornum2is "0", return "0". - Create a result array of length
m + n, initialized to all zeros. - Iterate
ifromm-1down to0(digits ofnum1from right to left):- For each
i, iteratejfromn-1down to0(digits ofnum2from right to left):- Compute
mul = digit(num1[i]) × digit(num2[j]). - Compute positions:
p1 = i + j,p2 = i + j + 1. - Compute
sum = mul + result[p2]. - Set
result[p2] = sum % 10. - Add carry:
result[p1] += sum / 10.
- Compute
- For each
- Convert the result array to a string, skipping any leading zeros.
- Return the result string.
Code
#include <string>
#include <vector>
using namespace std;
class Solution {
public:
string multiply(string num1, string num2) {
int m = num1.size(), n = num2.size();
if (num1 == "0" || num2 == "0") return "0";
vector<int> result(m + n, 0);
for (int i = m - 1; i >= 0; i--) {
for (int j = n - 1; j >= 0; j--) {
int mul = (num1[i] - '0') * (num2[j] - '0');
int p1 = i + j;
int p2 = i + j + 1;
int sum = mul + result[p2];
result[p2] = sum % 10;
result[p1] += sum / 10;
}
}
string str;
for (int digit : result) {
if (!(str.empty() && digit == 0)) {
str += to_string(digit);
}
}
return str.empty() ? "0" : str;
}
};class Solution:
def multiply(self, num1: str, num2: str) -> str:
if num1 == "0" or num2 == "0":
return "0"
m, n = len(num1), len(num2)
result = [0] * (m + n)
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
mul = (ord(num1[i]) - ord('0')) * (ord(num2[j]) - ord('0'))
p1 = i + j
p2 = i + j + 1
total = mul + result[p2]
result[p2] = total % 10
result[p1] += total // 10
# Convert to string, skip leading zeros
result_str = ''.join(str(d) for d in result)
return result_str.lstrip('0') or '0'class Solution {
public String multiply(String num1, String num2) {
int m = num1.length(), n = num2.length();
if (num1.equals("0") || num2.equals("0")) return "0";
int[] result = new int[m + n];
for (int i = m - 1; i >= 0; i--) {
for (int j = n - 1; j >= 0; j--) {
int mul = (num1.charAt(i) - '0') * (num2.charAt(j) - '0');
int p1 = i + j;
int p2 = i + j + 1;
int sum = mul + result[p2];
result[p2] = sum % 10;
result[p1] += sum / 10;
}
}
StringBuilder sb = new StringBuilder();
for (int digit : result) {
if (!(sb.length() == 0 && digit == 0)) {
sb.append(digit);
}
}
return sb.length() == 0 ? "0" : sb.toString();
}
}Complexity Analysis
Time Complexity: O(m × n)
We have two nested loops: the outer loop iterates through all m digits of num1, and the inner loop iterates through all n digits of num2. Each iteration performs O(1) work (one multiplication, one addition, one modulo, one division). The final string construction is O(m + n). Total: O(m × n).
With m, n ≤ 200, this is at most 40,000 operations — extremely fast.
Space Complexity: O(m + n)
We allocate a result array of size m + n to hold the digits of the product. The product of an m-digit number and an n-digit number has at most m + n digits (for example, 99 × 99 = 9801 has 4 = 2 + 2 digits). The output string also takes O(m + n) space. Total extra space: O(m + n).