LeetCode #43 — Medium

Multiply Strings

A complete visual walkthrough of grade-school multiplication using positional digit mapping.

Solve on LeetCode
The Problem

Problem Statement

Given two non-negative integers num1 and num2 represented as strings, return the product of num1 and num2, also represented as a string.

Note: You must not use any built-in BigInteger library or convert the inputs to integer directly.

Example 1:

Input: num1 = "2", num2 = "3"
Output: "6"

Example 2:

Input: num1 = "123", num2 = "456"
Output: "56088"

Constraints:

  • 1 <= num1.length, num2.length <= 200
  • num1 and num2 consist of digits only.
  • Both num1 and num2 do not contain any leading zero, except the number 0 itself.

Roadmap

  1. Why Not Convert to Integer?
  2. The Core Insight — Position Mapping
  3. Walkthrough — Multiplication Grid
  4. Edge Cases
  5. Full Annotated Code
  6. Interactive Demo
  7. Complexity Analysis
Step 01

Why Not Convert to Integer?

The most obvious approach is to convert both strings to integers, multiply them, and convert back. But this fails for a critical reason:

The Overflow Problem

The input strings can have up to 200 digits. Consider what happens:

int / long range
At most ~19 digits
Input can be
Up to 200 digits

Even BigInteger is explicitly forbidden. We need to simulate multiplication digit by digit, exactly like you learned in school.

💡

Key realization: We don't need the full number. We just need to process each pair of digits and accumulate their contributions at the correct positions in the result.

Step 02

The Core Insight — Position Mapping

When you multiply digit num1[i] by digit num2[j], the product always lands at specific positions in the result:

The Position Rule

For digits at indices i and j (0-indexed from left), their product contributes to positions i+j (carry/tens) and i+j+1 (ones) in the result array.

num1[i]
3
x
num2[j]
7
=
product = 21
2
pos[i+j]
1
pos[i+j+1]

Why does i+j work? Digit at index i from left in num1 represents 10^(m-1-i). Digit at index j represents 10^(n-1-j). Their product has magnitude 10^(m+n-2-i-j), which maps to position i+j (counting from the left of a result array of length m+n).

Step 03

Walkthrough — Multiplication Grid

Let's trace through "123" x "456" step by step, filling the result array of size m + n = 6.

Grade-School Multiplication Grid

Each cell shows num1[i] x num2[j] and where the product goes in the result array pos[0..5]:

4 (j=0) 5 (j=1) 6 (j=2)
1 (i=0) 4
p0,p1
5
p1,p2
6
p2,p3
2 (i=1) 8
p1,p2
10
p2,p3
12
p3,p4
3 (i=2) 12
p2,p3
15
p3,p4
18
p4,p5

We process from bottom-right (i=2, j=2) to top-left (i=0, j=0), adding each product into the result positions and carrying over as we go.

Building the Result Array

After processing all digit pairs (right-to-left), the pos array holds the final digits:

0
pos[0]
5
pos[1]
6
pos[2]
0
pos[3]
8
pos[4]
8
pos[5]
Skip leading zero → result = "56088"
Step 04

Edge Cases

Multiply by zero
"0" x anything = "0" — the result array will be all zeros. The final check sb.length() == 0 ? "0" : sb.toString() handles this.
Single digit x multi-digit
"9" x "99" = "891" — works naturally since the outer loop runs once, inner loop processes each digit of the longer number.
Leading zeros in result
Result array size is m+n, but actual product may have fewer digits (e.g., "10" x "10" = "100" uses 3 of 4 positions). We skip leading zeros when building the string.
Step 05

Full Annotated Code

class Solution {
    public String multiply(String num1, String num2) {

        int m = num1.length(), n = num2.length();
        int[] pos = new int[m + n];   // result digits; max length is m+n

        // Process each digit pair, right to left
        for (int i = m - 1; i >= 0; i--) {
            for (int j = n - 1; j >= 0; j--) {

                // Multiply single digits
                int mul = (num1.charAt(i) - '0') * (num2.charAt(j) - '0');

                // Target positions in result
                int p1 = i + j, p2 = i + j + 1;

                // Add to existing value (handles carry accumulation)
                int sum = mul + pos[p2];

                pos[p2] = sum % 10;      // ones digit stays at p2
                pos[p1] += sum / 10;     // carry goes to p1
            }
        }

        // Build result string, skipping leading zeros
        StringBuilder sb = new StringBuilder();
        for (int p : pos)
            if (!(sb.length() == 0 && p == 0)) sb.append(p);

        return sb.length() == 0 ? "0" : sb.toString();
    }
}
Step 06

Interactive Demo

Enter two number strings and step through the grade-school multiplication grid cell by cell.

Grade-School Multiplication Visualizer



Step 07

Complexity Analysis

Time
O(m × n)
Space
O(m + n)

We multiply every digit of num1 (length n) with every digit of num2 (length m), performing O(1) work per pair. The result is stored in an array of length n + m, because the product of an n-digit and m-digit number has at most n + m digits.

Approach Breakdown

BRUTE FORCE
O(n × m) time
O(n + m) space

Two nested loops iterate every digit of num1 (length n) against every digit of num2 (length m), performing one single-digit multiplication per pair. The result array has at most n + m positions, so space is O(n + m). Grade-school multiplication is inherently O(n × m).

OPTIMAL
O(n × m) time
O(n + m) space

Same nested-loop structure, but the position-mapping trick (i+j, i+j+1) accumulates carries in place, avoiding intermediate string conversions. Every digit pair must be examined, so O(n × m) is a lower bound. The result array of size n + m is the only extra allocation.

🎯

The product of an n-digit and m-digit number has at most n + m digits -- pre-allocate the result array to that size. Grade-school multiplication must examine every digit pair, so O(n × m) is inherent. Faster algorithms like Karatsuba (O(n1.585)) exist but are overkill for this problem's constraints.

Coach Notes

Common Mistakes

Review these before coding to avoid predictable interview regressions.

Overflow in intermediate arithmetic

Wrong move: Temporary multiplications exceed integer bounds.

Usually fails on: Large inputs wrap around unexpectedly.

Fix: Use wider types, modular arithmetic, or rearranged operations.