Multiply two non-negative integers represented as strings without converting them to BigInt or native numbers. The key insight is that digit i of num1 times digit j of num2 always lands in exactly one slot of the result — no guessing needed.
Given two non-negative integers as strings num1 and num2, return their product as a string — no built-in big-integer conversions allowed. Example: num1="23", num2="45" → "1035". Another: num1="999", num2="999" → "998001".
i of num1 by digit at index j of num2, the product's units go to result slot i+j+1 and the tens (carry) go to slot i+j — always, regardless of value. Allocate a result[m+n] array, accumulate every product into its two slots, and resolve carries in one final left-to-right pass that is already done inside the inner loop.res = new Array(m+n).fill(0). A product of an m-digit and n-digit number has at most m+n digits.i, grab the digit value d1 = num1.charCodeAt(i) - 48.j, compute mul = d1 * (num2.charCodeAt(j) - 48).mul to res[i+j+1], then propagate the carry: res[i+j] += Math.floor(sum / 10). Because we loop from right-to-left, by the time we touch slot i+j again its carry has already landed.res.join('').replace(/^0+/, ''). Guard for all-zero by returning "0" when the result is empty.i+j (carry slot) and i+j+1 (units slot). Think of it this way: if i=0, j=0 and both digits are 9, the product 81 writes 1 to slot 1 (units) and adds 8 to slot 0 (carry). Slot i+j+1 is always the units slot.The result array is always exactly m+n slots — O(m+n) space. No extra carry buffer is needed because we accumulate carries in-place. The FFT-based Karatsuba algorithm can reach O(n log n) but is never expected in interviews.
23 × 45. Allocate res[4] filled with zeros (m+n = 2+2).1▶function multiply(num1: string, num2: string): string {2▶ const m = num1.length, n = num2.length;3▶ const res = new Array<number>(m + n).fill(0);45 // schoolbook: digit i * digit j lands at positions i+j and i+j+16 for (let i = m - 1; i >= 0; i--) {7 for (let j = n - 1; j >= 0; j--) {8 const mul = (num1.charCodeAt(i) - 48) * (num2.charCodeAt(j) - 48);9 const p1 = i + j; // carry position10 const p2 = i + j + 1; // units position11 const sum = mul + res[p2];12 res[p2] = sum % 10;13 res[p1] += Math.floor(sum / 10);14 }15 }1617 // strip leading zeros, then join18 const result = res.join('').replace(/^0+/, '');19 return result === '' ? '0' : result;20}
function multiply(num1: string, num2: string): string {
const m = num1.length, n = num2.length;
const res = new Array<number>(m + n).fill(0);
// schoolbook: digit i * digit j lands at positions i+j and i+j+1
for (let i = m - 1; i >= 0; i--) {
for (let j = n - 1; j >= 0; j--) {
const mul = (num1.charCodeAt(i) - 48) * (num2.charCodeAt(j) - 48);
const p1 = i + j; // carry position
const p2 = i + j + 1; // units position
const sum = mul + res[p2];
res[p2] = sum % 10;
res[p1] += Math.floor(sum / 10);
}
}
// strip leading zeros, then join
const result = res.join('').replace(/^0+/, '');
return result === '' ? '0' : result;
}num1 so that carry propagation flows naturally toward the front of the array. Using charCodeAt(i) - 48 converts '0'–'9' to 0–9 without parseInt.(i, j), mul is the raw product (0–81). We add it to res[p2] (the units slot), take modulo 10 to keep just the digit, and propagate the tens to res[p1]. This works without a separate carry-resolve pass because we always move the carry left (toward smaller indices), and we process right-to-left.join('') converts the digit array to a string. The regex strips leading zeros. If the result is the empty string (input was '0'), we return '0'.reverse(). See Add Strings."0".i from the right has place value 10^i. Processing right-to-left means the carry always goes to a slot we'll visit again (or have already accumulated into), keeping the in-place propagation correct.function multiply(num1: string, num2: string): string {
const m = num1.length, n = num2.length;
const res = new Array<number>(m + n).fill(0);
// schoolbook: digit i * digit j lands at positions i+j and i+j+1
for (let i = m - 1; i >= 0; i--) {
for (let j = n - 1; j >= 0; j--) {
const mul = (num1.charCodeAt(i) - 48) * (num2.charCodeAt(j) - 48);
const p1 = i + j; // carry position
const p2 = i + j + 1; // units position
const sum = mul + res[p2];
res[p2] = sum % 10;
res[p1] += Math.floor(sum / 10);
}
}
// strip leading zeros, then join
const result = res.join('').replace(/^0+/, '');
return result === '' ? '0' : result;
}Spell out grade-school long multiplication literally: for each digit of num2 build the full partial product (digit × num1, shifted by its place), then add all the partial products together with a string adder. No clever index math — just the steps you'd write on paper.
function multiply(num1: string, num2: string): string {
if (num1 === '0' || num2 === '0') return '0';
// Add two non-negative integer strings, digit by digit.
function addStrings(a: string, b: string): string {
let i = a.length - 1, j = b.length - 1, carry = 0;
let out = '';
while (i >= 0 || j >= 0 || carry > 0) {
const da = i >= 0 ? a.charCodeAt(i--) - 48 : 0;
const db = j >= 0 ? b.charCodeAt(j--) - 48 : 0;
const sum = da + db + carry;
out = String(sum % 10) + out;
carry = Math.floor(sum / 10);
}
return out;
}
let result = '0';
// num2's last digit has place 0, so it gets that many trailing zeros.
for (let j = num2.length - 1; j >= 0; j--) {
const d = num2.charCodeAt(j) - 48;
const place = num2.length - 1 - j;
// Build partial = d * num1, single-digit times the whole number.
let carry = 0;
let partial = '';
for (let i = num1.length - 1; i >= 0; i--) {
const prod = d * (num1.charCodeAt(i) - 48) + carry;
partial = String(prod % 10) + partial;
carry = Math.floor(prod / 10);
}
if (carry > 0) partial = String(carry) + partial;
partial += '0'.repeat(place); // shift by its decimal place
result = addStrings(result, partial); // accumulate
}
return result;
}(m+n)-length string per digit of num2 and re-runs a string adder n times, so it does noticeably more allocation and work than the single in-place result array. The schoolbook method folds every digit-pair directly into one buffer in O(m·n), avoiding the repeated additions entirely.| "multiply/add large integers as strings" | result[m+n] array, digit-pair loops |
| arithmetic on digits without overflow | charCodeAt(i) - 48, index math |
| "no BigInt / parseInt allowed" | schoolbook array multiply |
| result at most m+n digits wide | allocate m+n zeros, strip leading zeros |
function multiply(num1: string, num2: string): string {
const m = num1.length, n = num2.length;
const res = new Array<number>(m + n).fill(0);
for (let i = m - 1; i >= 0; i--) {
for (let j = n - 1; j >= 0; j--) {
const mul = (num1.charCodeAt(i) - 48) * (num2.charCodeAt(j) - 48);
const sum = mul + res[i + j + 1];
res[i + j + 1] = sum % 10;
res[i + j] += Math.floor(sum / 10);
}
}
const result = res.join('').replace(/^0+/, '');
return result === '' ? '0' : result;
}m + n — the product of an m-digit and n-digit number has at most m+n digits.num1="23" (m=2) and num2="45" (n=2), what size is the result array?i=1 of num1 and digit j=0 of num2, where does the units digit of their product land?"2" × "3": after the double loop what does res contain?"0" when the stripped string is empty?'7' to the integer 7 most efficiently?