Add Strings
EasyPrompt
Given two non-negative integers, num1 and num2 represented as string, return the sum of num1 and num2 as a string.
You must solve the problem without using any built-in library for handling large integers (such as BigInteger). You must also not convert the inputs to integers directly.
Example 1:
Input: num1 = "11", num2 = "123"
Output: "134"Example 2:
Input: num1 = "456", num2 = "77"
Output: "533"Example 3:
Input: num1 = "0", num2 = "0"
Output: "0"
Constraints:
1 <= num1.length, num2.length <= 104num1andnum2consist of only digits.num1andnum2don't have any leading zeros except for the zero itself.
Approaches
2 approaches with complexity analysis and trade-offs.
This method mimics the way humans add numbers on paper, starting from the least significant digit (the rightmost one). It iteratively calculates the sum of digits at each position, manages a carry-over, and builds the result string. However, it uses an inefficient way to build the string by prepending digits, which degrades performance significantly.
Algorithm
- Initialize
result = "",i = num1.length() - 1,j = num2.length() - 1,carry = 0. - Loop while
i >= 0orj >= 0orcarry > 0. - Extract
digit1fromnum1ati(or 0 ifi < 0). - Extract
digit2fromnum2atj(or 0 ifj < 0). - Calculate
sum = digit1 + digit2 + carry. - Update
carry = sum / 10. - Prepend
sum % 10to theresultstring. - Decrement
iandj. - Return
result.
Walkthrough
The algorithm proceeds as follows:
- Initialize two pointers,
iandj, to point to the last character ofnum1andnum2, respectively. - Initialize a
carryvariable to0. - Initialize an empty string,
result, to store the sum. - Loop as long as
iorjare valid indices, or if there is acarryleft over. - Inside the loop:
- Get the integer value of the digit from
num1at indexi. Ifiis out of bounds, use0. - Get the integer value of the digit from
num2at indexj. Ifjis out of bounds, use0. - Calculate the
currentSum = digit1 + digit2 + carry. - The new
carryfor the next iteration iscurrentSum / 10. - The digit for the current position is
currentSum % 10. - Prepend this digit to the
resultstring. In Java,result = (currentSum % 10) + result;. This is anO(L)operation whereLis the current length ofresult, as strings are immutable. - Decrement
iandjto move to the next digits to the left.
- Get the integer value of the digit from
- After the loop,
resultholds the final sum. A special case is if both input strings are "0", the result should be "0". The algorithm handles this correctly.
class Solution { public String addStrings(String num1, String num2) { String result = ""; int i = num1.length() - 1; int j = num2.length() - 1; int carry = 0; while (i >= 0 || j >= 0 || carry > 0) { int digit1 = (i >= 0) ? num1.charAt(i) - '0' : 0; int digit2 = (j >= 0) ? num2.charAt(j) - '0' : 0; int sum = digit1 + digit2 + carry; carry = sum / 10; int currentDigit = sum % 10; // Inefficient string concatenation result = currentDigit + result; i--; j--; } return result; }}Complexity
Time
`O(max(N, M)^2)`, where `N` and `M` are the lengths of `num1` and `num2`. Prepending a character to a string of length `k` takes `O(k)` time. Since this is done in a loop `max(N, M)` times, the total time complexity becomes quadratic.
Space
`O(max(N, M)^2)`. In each iteration, a new string is created for the result. The sum of the lengths of all these intermediate strings is `1 + 2 + ... + max(N, M)`, which results in quadratic space complexity.
Trade-offs
Pros
The logic is very straightforward and easy to understand as it directly simulates manual addition.
Cons
Extremely inefficient for long input strings due to quadratic time and space complexity from repeated string concatenations.
Will likely result in a "Time Limit Exceeded" or "Memory Limit Exceeded" error on platforms with strict constraints.
This approach also simulates manual, right-to-left addition but uses a StringBuilder for efficient string construction. By appending digits to a StringBuilder and reversing it once at the end, we avoid the costly re-creation of strings in a loop.
Algorithm
- Initialize
StringBuilder result,i = num1.length() - 1,j = num2.length() - 1,carry = 0. - Loop while
i >= 0orj >= 0orcarry > 0. - Extract
digit1fromnum1ati(or 0 ifi < 0) and decrementi. - Extract
digit2fromnum2atj(or 0 ifj < 0) and decrementj. - Calculate
sum = digit1 + digit2 + carry. - Append
sum % 10to theresultStringBuilder. - Update
carry = sum / 10. - After the loop, reverse the
resultStringBuilder. - Return the string representation of the reversed
result.
Walkthrough
This method is an optimized version of the previous one. The core idea of simulating grade-school addition remains the same.
- Initialize a
StringBuilderto build the result string. - Initialize pointers
iandjto the last characters ofnum1andnum2. - Initialize
carryto0. - Loop as long as there are digits in either
num1ornum2to process, or if acarryexists. - Inside the loop:
- Get the integer values of the digits from
num1andnum2at the current pointers. If a pointer is out of bounds, its corresponding digit is0. - Calculate
sum = digit1 + digit2 + carry. - The digit for the current position is
sum % 10. Append this to theStringBuilder. Appending to aStringBuilderis an amortizedO(1)operation. - The new
carryfor the next position issum / 10. - Decrement the pointers
iandj.
- Get the integer values of the digits from
- After the loop, the
StringBuildercontains the digits of the sum, but in reverse order (from least significant to most significant). - Reverse the
StringBuilderand convert it to a string to get the final correct result.
class Solution { public String addStrings(String num1, String num2) { StringBuilder result = new StringBuilder(); int i = num1.length() - 1; int j = num2.length() - 1; int carry = 0; while (i >= 0 || j >= 0 || carry > 0) { int digit1 = (i >= 0) ? num1.charAt(i--) - '0' : 0; int digit2 = (j >= 0) ? num2.charAt(j--) - '0' : 0; int sum = digit1 + digit2 + carry; carry = sum / 10; result.append(sum % 10); } return result.reverse().toString(); }}Complexity
Time
`O(max(N, M))`, where `N` and `M` are the lengths of `num1` and `num2`. The algorithm iterates through the digits of the strings once. Appending to a `StringBuilder` is amortized `O(1)`, and the final reversal takes `O(max(N, M))` time.
Space
`O(max(N, M))`. The `StringBuilder` used to store the result will have a length of at most `max(N, M) + 1`.
Trade-offs
Pros
Optimal time and space complexity for this problem.
Efficiently handles very large numbers within the given constraints.
The logic is still relatively simple and follows the natural process of addition.
Cons
Requires an extra step to reverse the string at the end, which adds a small constant factor overhead but doesn't change the overall complexity.
Solutions
Solution
class Solution {public String addStrings(String num1, String num2) { int i = num1.length() - 1, j = num2.length() - 1; StringBuilder ans = new StringBuilder(); for (int c = 0; i >= 0 || j >= 0 || c > 0; --i, --j) { int a = i < 0 ? 0 : num1.charAt(i) - '0'; int b = j < 0 ? 0 : num2.charAt(j) - '0'; c += a + b; ans.append(c % 10); c /= 10; } return ans.reverse().toString(); }public String subStrings(String num1, String num2) { int m = num1.length(), n = num2.length(); boolean neg = m < n || (m == n && num1.compareTo(num2) < 0); if (neg) { String t = num1; num1 = num2; num2 = t; } int i = num1.length() - 1, j = num2.length() - 1; StringBuilder ans = new StringBuilder(); for (int c = 0; i >= 0; --i, --j) { c = (num1.charAt(i) - '0') - c - (j < 0 ? 0 : num2.charAt(j) - '0'); ans.append((c + 10) % 10); c = c < 0 ? 1 : 0; } while (ans.length() > 1 && ans.charAt(ans.length() - 1) == '0') { ans.deleteCharAt(ans.length() - 1); } if (neg) { ans.append('-'); } return ans.reverse().toString(); }}/** * @param {string} num1 * @param {string} num2 * @return {string} */ var addStrings = function (num1, num2) { let i = num1.length - 1; let j = num2.length - 1; const ans = []; for (let c = 0; i >= 0 || j >= 0 || c; --i, --j) { c += i < 0 ? 0 : +num1[i]; c += j < 0 ? 0 : +num2[j]; ans.push(c % 10); c = Math.floor(c / 10); } return ans.reverse().join(""); };/** * @param {string} num1 * @param {string} num2 * @return {string} */ var subStrings = function (num1, num2) { const m = num1.length; const n = num2.length; const neg = m < n || (m == n && num1 < num2); if (neg) { const t = num1; num1 = num2; num2 = t; } let i = num1.length - 1; let j = num2.length - 1; const ans = []; for (let c = 0; i >= 0; --i, --j) { c = +num1[i] - c; if (j >= 0) { c -= +num2[j]; } ans.push((c + 10) % 10); c = c < 0 ? 1 : 0; } while (ans.length > 1 && ans.at(-1) === 0) { ans.pop(); } return (neg ? " - " : "") + ans.reverse().join(""); };class Solution {public: string addStrings(string num1, string num2) { int i = num1.size() - 1, j = num2.size() - 1; string ans; for (int c = 0; i >= 0 || j >= 0 || c; --i, --j) { int a = i < 0 ? 0 : num1[i] - '0'; int b = j < 0 ? 0 : num2[j] - '0'; c += a + b; ans += to_string(c % 10); c /= 10; } reverse(ans.begin(), ans.end()); return ans; } string subStrings(string num1, string num2) { int m = num1.size(), n = num2.size(); bool neg = m < n || (m == n && num1 < num2); if (neg) { swap(num1, num2); } int i = num1.size() - 1, j = num2.size() - 1; string ans; for (int c = 0; i >= 0; --i, --j) { c = (num1[i] - '0') - c - (j < 0 ? 0 : num2[j] - '0'); ans += to_string((c + 10) % 10); c = c < 0 ? 1 : 0; } while (ans.size() > 1 && ans.back() == '0') { ans.pop_back(); } if (neg) { ans.push_back('-'); } reverse(ans.begin(), ans.end()); return ans; }};class Solution: def addStrings(self, num1: str, num2: str) -> str: i, j = len(num1) - 1, len(num2) - 1 ans = [] c = 0 while i >= 0 or j >= 0 or c: a = 0 if i < 0 else int(num1[i]) b = 0 if j < 0 else int(num2[j]) c, v = divmod(a + b + c, 10) # nice ans . append ( str ( v )) i , j = i - 1 , j - 1 return "" . join ( ans [:: - 1 ]) # follow-up, substract def subStrings ( self , num1 : str , num2 : str ) -> str : m , n = len ( num1 ), len ( num2 ) neg = m < n or ( m == n and num1 < num2 ) if neg : num1 , num2 = num2 , num1 i , j = len ( num1 ) - 1 , len ( num2 ) - 1 ans = [] c = 0 while i >= 0 : c = int ( num1 [ i ]) - c - ( 0 if j < 0 else int ( num2 [ j ])) ans . append ( str (( c + 10 ) % 10 )) c = 1 if c < 0 else 0 i , j = i - 1 , j - 1 # eg. 99199 - 99198 = 1, ans here is "10000" while len ( ans ) > 1 and ans [ - 1 ] == "0" : ans . pop () if neg : # will not be "-0", neg only when <0 ans . append ( "-" ) return "" . join ( ans [:: - 1 ]) ############ class Solution : def addStrings ( self , num1 : str , num2 : str ) -> str : i , j = len ( num1 ) - 1 , len ( num2 ) - 1 ans = [] c = 0 while i >= 0 or j >= 0 or c : a = 0 if i < 0 else int ( num1 [ i ]) b = 0 if j < 0 else int ( num2 [ j ]) c , v = divmod ( a + b + c , 10 ) ans . append ( str ( v )) i , j = i - 1 , j - 1 return "" . join ( ans [:: - 1 ]) def subStrings ( self , num1 : str , num2 : str ) -> str : m , n = len ( num1 ), len ( num2 ) neg = m < n or ( m == n and num1 < num2 ) if neg : num1 , num2 = num2 , num1 i , j = len ( num1 ) - 1 , len ( num2 ) - 1 ans = [] c = 0 while i >= 0 : c = int ( num1 [ i ]) - c - ( 0 if j < 0 else int ( num2 [ j ])) ans . append ( str (( c + 10 ) % 10 )) c = 1 if c < 0 else 0 i , j = i - 1 , j - 1 while len ( ans ) > 1 and ans [ - 1 ] == "0" : ans . pop () if neg : ans . append ( "-" ) return "" . join ( ans [:: - 1 ])Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.