Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

9.13 Add Strings

Source: src/main/kotlin/math/AddStrings.kt Pattern: digit-wise addition without conversion · Core page

The Problem

Add two non-negative integer strings (num1, num2) without converting to integers (they can be huge).

  • Constraints: $1 \le$ length ≤ 10⁴; digits only.

Examples

Input:  num1 = "11", num2 = "123"   -> Output: "134"
Input:  num1 = "456", num2 = "77"   -> Output: "533"

Intuition — the 4.7 carry loop, on strings

Identical to the linked-list addition — right-to-left, % 10 / / 10 carry — except digits come from char - '0' and the result is a StringBuilder (built reversed, then reversed back):

var (i, j) = num1.length - 1 to num2.length - 1
var carry = 0
val sb = StringBuilder()

while (i >= 0 || j >= 0 || carry != 0) {
    val digit1 = if (i >= 0) num1[i--] - '0' else 0
    val digit2 = if (j >= 0) num2[j--] - '0' else 0
    val sum = digit1 + digit2 + carry
    carry = sum / 10
    sb.append(sum % 10)
}
return sb.reverse().toString()

Why char - '0'? '5' - '0' = 5 — the ASCII-offset digit extraction, the string-side twin of node.val. The if (i >= 0) elvis gives missing digits the value 0.

Why build reversed then reverse()? Digits are appended least-significant first; the string must be most-significant first. One final reversal fixes the order — the same “accumulate backward, flip once” pattern as 4.7’s dummy-head (there the order is native, here it isn’t).

Approach 1 — BigInteger / toLong() (overflow!)

Convert and add: breaks on 10⁴-digit inputs — the problem’s hidden constraint.

Approach 2 — Digit-wise carry (the repo’s version, optimal)

class AddStrings {
    /**
     * @param num1 first number as a string
     * @param num2 second number as a string
     * @return     their sum as a string
     */
    fun addStrings(num1: String, num2: String): String {
        val sb = StringBuilder()
        var (i, j) = num1.length - 1 to num2.length - 1
        var carry = 0

        while (i >= 0 || j >= 0 || carry != 0) {
            val digit1 = if (i >= 0) num1[i--] - '0' else 0
            val digit2 = if (j >= 0) num2[j--] - '0' else 0

            val sum = digit1 + digit2 + carry
            carry = sum / 10
            sb.append(sum % 10)
        }
        return sb.reverse().toString()
    }
}
public class AddStrings {
    /**
     * @param num1 first number as a string
     * @param num2 second number as a string
     * @return     their sum as a string
     */
    public String addStrings(String num1, String num2) {
        StringBuilder sb = new StringBuilder();
        int i = num1.length() - 1, j = num2.length() - 1, carry = 0;

        while (i >= 0 || j >= 0 || carry != 0) {
            int d1 = i >= 0 ? num1.charAt(i--) - '0' : 0;
            int d2 = j >= 0 ? num2.charAt(j--) - '0' : 0;
            int sum = d1 + d2 + carry;
            carry = sum / 10;
            sb.append(sum % 10);
        }
        return sb.reverse().toString();
    }
}
#include <string>
#include <algorithm>

class AddStrings {
public:
    /**
     * @param num1 first number as a string
     * @param num2 second number as a string
     * @return     their sum as a string
     */
    std::string addStrings(std::string num1, std::string num2) {
        std::string result;
        int i = num1.size() - 1, j = num2.size() - 1, carry = 0;

        while (i >= 0 || j >= 0 || carry) {
            int d1 = i >= 0 ? num1[i--] - '0' : 0;
            int d2 = j >= 0 ? num2[j--] - '0' : 0;
            int sum = d1 + d2 + carry;
            carry = sum / 10;
            result += (char)('0' + sum % 10);
        }
        std::reverse(result.begin(), result.end());
        return result;
    }
};
def add_strings(num1: str, num2: str) -> str:
    """
    @param num1: first number as a string
    @param num2: second number as a string
    @return:     their sum as a string
    """
    i, j = len(num1) - 1, len(num2) - 1
    carry = 0
    result = []

    while i >= 0 or j >= 0 or carry:
        d1 = int(num1[i]) if i >= 0 else 0
        d2 = int(num2[j]) if j >= 0 else 0
        total = d1 + d2 + carry
        carry = total // 10
        result.append(str(total % 10))
        i -= 1
        j -= 1

    return "".join(reversed(result))
#![allow(unused)]
fn main() {
impl Solution {
    /// @param num1 first number as a string
    /// @param num2 second number as a string
    /// @return     their sum as a string
    pub fn add_strings(num1: String, num2: String) -> String {
        let (b1, b2) = (num1.as_bytes(), num2.as_bytes());
        let (mut i, mut j) = (b1.len() as i32 - 1, b2.len() as i32 - 1);
        let mut carry = 0u8;
        let mut result = String::new();

        while i >= 0 || j >= 0 || carry > 0 {
            let d1 = if i >= 0 { b1[i as usize] - b'0' } else { 0 };
            let d2 = if j >= 0 { b2[j as usize] - b'0' } else { 0 };
            let sum = d1 + d2 + carry;
            carry = sum / 10;
            result.push((b'0' + sum % 10) as char);
            i -= 1;
            j -= 1;
        }
        result.chars().rev().collect()
    }
}
}

Dry run

Input: num1 = "456", num2 = "77".

i=2 ('6'), j=1 ('7'), carry=0: sum = 6+7+0 = 13.  carry=1.  sb="3".  i=1, j=0
i=1 ('5'), j=0 ('7'), carry=1: sum = 5+7+1 = 13.  carry=1.  sb="33".  i=0, j=-1
i=0 ('4'), j<0,     carry=1: sum = 4+0+1 = 5.   carry=0.  sb="335".  i=-1
i<0, j<0, carry=0 -> stop.  reverse: "533" ✓

The carry propagation through the unequal lengths is the whole story: the if (j >= 0) elvis gives the missing tens-digit a 0, and the final carry = 1 (from 13) rolls into the hundreds. "11" + "123""134" the same way. BigInteger would break at 10⁴ digits; this loop never does.

Complexity

Time. One pass over the longer string:

$$ T(n) = O(\max(|num1|, |num2|)) $$

Space. The result builder:

$$ S = O(\max(|num1|, |num2|)) $$

Variants & follow-ups

  • Add Two Numbers (4.7) — the linked-list twin; identical carry loop.
  • Multiply Strings (math/MultiplyStrings.kt) — the multiplication upgrade: per-digit products into a running array.
  • Interview follow-up: “Why is char - '0' the right extraction?” Digits in ASCII are contiguous — '0' is 48, '9' is 57, so '5' - '0' = 5. It’s the zero-cost integer conversion that makes string math feasible at 10⁴ digits.