Add Strings: Digit-by-Digit Addition Without Converting
Add Strings is LeetCode 415. You are given two non-negative integers represented as strings num1 and num2, and you need to return their sum as a string. The constraint: you cannot use any built-in BigInteger library or convert the inputs to integers directly.
Example: num1 = "456", num2 = "77" returns "533".
Why this problem matters
The natural instinct is to convert both strings to integers, add them, and convert back. But the problem explicitly forbids this. Even if it did not, the strings can be very long, potentially exceeding what a 64-bit integer can hold. You need an approach that works for arbitrarily large numbers.
This forces you to think about how addition actually works at the digit level. You already know the algorithm, because you learned it in elementary school: line up the numbers on the right, add column by column, and carry the overflow. The only difference here is that the digits live inside strings instead of on paper.
The digit-by-digit addition pattern shows up in several other problems. Once you can add two decimal strings, you can add binary strings, add numbers stored in linked lists, and even multiply strings. This problem is the cleanest version of that pattern.
The approach
Start two pointers at the rightmost character of each string and walk them backward. At each position, convert the characters to digits, add them along with any carry from the previous column, and compute the result digit and the new carry. When one string runs out before the other, treat its missing positions as 0. After both strings are exhausted, check whether a final carry remains.
The result is built in reverse order (least significant digit first), so you reverse it at the end.
def addStrings(num1: str, num2: str) -> str:
i, j = len(num1) - 1, len(num2) - 1
carry = 0
result = []
while i >= 0 or j >= 0 or carry:
total = carry
if i >= 0:
total += ord(num1[i]) - ord('0')
i -= 1
if j >= 0:
total += ord(num2[j]) - ord('0')
j -= 1
result.append(str(total % 10))
carry = total // 10
return ''.join(reversed(result))
Step-by-step walkthrough
Here is the addition of num1 = "456" and num2 = "77", which produces "533". Watch how the carry propagates through each column and how the shorter string is handled once it runs out.
Step 1: Rightmost column. 6 + 7 = 13. Write 3, carry 1.
num1[2] + num2[1] + 0 (carry) = 6 + 7 + 0 = 13. Write 13 % 10 = 3, carry becomes 13 // 10 = 1.
Step 2: Middle column. 5 + 7 + 1 (carry) = 13. Write 3, carry 1.
num1[1] + num2[0] + 1 (carry) = 5 + 7 + 1 = 13. Write 3, carry becomes 1.
Step 3: Left column. num2 is exhausted. 4 + 0 + 1 (carry) = 5. Write 5, carry 0.
num1[0] + 0 (num2 exhausted) + 1 (carry) = 4 + 0 + 1 = 5. Write 5, carry becomes 0.
Step 4: Both strings exhausted, carry is 0. Done. Reverse to get "533".
No digits remain and carry is 0. The result list [3, 3, 5] is reversed to produce "533".
Each step follows the same logic: grab the current digit from each string (or 0 if that string is exhausted), add them with the carry, write total % 10, and update the carry to total // 10. The loop body never changes regardless of string lengths or carry values.
Notice the use of ord(char) - ord('0') instead of int(char). Both work, but the ord approach avoids calling Python's integer parser and makes the character-to-digit conversion explicit. Either option is fine in an interview.
Complexity analysis
| Approach | Time | Space |
|---|---|---|
| Digit-by-digit addition | O(max(m, n)) | O(max(m, n)) |
Time: O(max(m, n)) where m and n are the lengths of num1 and num2. You visit each character in both strings exactly once. The final carry step adds at most one more iteration.
Space: O(max(m, n)) for the result list. The output has at most max(m, n) + 1 characters (the +1 accounts for a potential final carry digit).
Edge cases to watch for
- Different lengths.
num1 = "1",num2 = "9999". The shorter string runs out first. You treat its missing positions as 0, so the remaining digits of the longer string are added with just the carry. - Trailing carry.
num1 = "1",num2 = "9"returns"10". Both strings are exhausted after one column, but the leftover carry of 1 produces an extra leading digit. Theor carryin the loop condition handles this. - Both are "0".
num1 = "0",num2 = "0"returns"0". The sum is 0 with no carry, producing a single"0". - Large inputs. Strings with thousands of digits. The algorithm handles them naturally because it never converts the entire string to an integer.
The building blocks
This problem is built from two core building blocks:
Right-to-left digit processing. Walk two sequences from their ends toward their beginnings, processing one position at a time. When one sequence is shorter, treat its missing positions as 0. This same traversal pattern appears in Add Binary (base 2 instead of base 10), Add Two Numbers (linked lists instead of strings), and Multiply Strings (where each digit of one number multiplies every digit of the other).
Carry propagation. At each position, compute a total and split it into a result digit (total % base) and a carry (total // base). The carry feeds into the next column. After all positions are processed, flush any remaining carry. The logic is identical whether you work in base 2, base 10, or any other base. The only thing that changes is the modulus.
These two building blocks combine to handle the entire problem. The pointer logic handles traversal. The carry arithmetic handles the math. Each one is simple in isolation, and together they cover every case.
From understanding to recall
Reading through this solution and understanding each line is the easy part. Reproducing it from memory under time pressure is what matters. The ord conversion, the or carry loop condition, the % 10 and // 10 arithmetic, the final reverse. Each detail is small, but forgetting any one of them gives a wrong answer.
Spaced repetition locks this in. Type the solution from scratch today, again in a few days, again next week. After a few reps, the loop structure and carry logic become automatic.
Related posts
- Add Binary - The same carry-based digit addition in base 2 instead of base 10. If you can solve Add Strings, you already know the core of Add Binary.
- Add Two Numbers - Carry-based digit addition on linked lists instead of strings. The arithmetic is identical, but you build nodes instead of appending to a list.
- Multiply Strings - A harder extension of digit-by-digit arithmetic where you multiply every pair of digits and accumulate partial products.