leetcode / Math and geometry / Number math core

Number math

Some problems are arithmetic in disguise: take a number apart digit by digit, put it back together in another order or base, multiply numbers too big to store, or raise something to a huge power. The tools are old and small, divmod by ten, a carry, a bound checked before it is crossed, and squaring instead of repeating, and each one turns a slow or overflowing answer into a fast and exact one.

On this page
  1. Problem
  2. Intuition
  3. Mechanics
  4. Peeling and building digits
  5. Checking the bound before crossing it
  6. Reversing only half
  7. Square-and-multiply
  8. Multiplying digit strings
  9. Other bases and notations
  10. Worked example
  11. Implementation
  12. Variations and edge cases
  13. Complexity and tradeoffs
  14. Problems
  15. Easy
  16. Medium
  17. Also uses this pattern
  18. Connections
  19. Read first
  20. Read next
  21. Check yourself
  22. References

Problem

These questions hand you a number, or a string of digits, and ask you to transform it: reverse it, test it for symmetry, convert it to or from another notation, add one, multiply two of them, or compute a power. The obvious answers either convert to a string and back (often banned, or hiding the point), loop n times when n is a billion, or quietly overflow a 32-bit integer that the problem says you must respect. The pattern is to treat a number as a sequence of digits in some base, process it from the cheap end (the last digit, the lowest bit), and keep every intermediate value inside its stated range.

SignalPatternWhy
"Reverse the digits", "is it a palindrome", "sum of digits"Peel digits with divmod(x, 10)x % 10 is the last digit and x // 10 drops it, so a loop visits every digit in O(log x).
"Assume a 32-bit environment", "return 0 if it overflows"Check the bound before multiplying by tenOnce the value has overflowed in a fixed-width language it is already wrong; compare with INT_MAX // 10 first.
"Compute xn" with n up to 231Square-and-multiplyThe bits of n say which squares of x to multiply, so the work is O(log n) instead of n.
"Numbers given as strings", "do not convert to integers"Grade-school arithmetic on digit arraysDigit i times digit j lands at a known position, so carries can be settled in one array.
"Column title", "Roman numerals", "base 7"Base conversion with the right digit setEach notation is a positional (or additive) code; find its digits and its zero, if it has one.
"Greatest common", "repeats with period", "lattice points"Euclid's gcdgcd(a, b) = gcd(b, a mod b) shrinks the pair at least as fast as Fibonacci numbers grow.

Intuition

A number written in base ten is a list of digits wearing a disguise. x % 10 reads the last digit, x // 10 removes it, and r * 10 + d appends d to r. With those three moves you can pop digits off one number and push them onto another, which reverses it, and you never need a string. The only thing to watch is the size of what you are building: in Python integers never overflow, but the problems are set for languages where they do, and the interviewer wants to see you check the bound before the multiply that would break it.

Powers work the same way in base two. Thirteen is 1101 in binary, so x13 = x8 · x4 · x1. The powers x, x2, x4, x8 each come from squaring the one before, so you only need as many squarings as n has bits, and you multiply in the ones whose bit is set.

Computing 3 to the 13th by square-and-multiply The exponent 13 has bits 1101, read from the lowest. The running power of 3 is 3, 9, 81, 6561; the powers whose bit is 1 are multiplied into the result, which goes 3, 3, 243, 1594323. The answer 1594323 took 6 multiplications instead of 12. bit 3 (2^3 = 8) 1 3^8 = 6561 multiply in result 1594323 bit 2 (2^2 = 4) 1 3^4 = 81 multiply in result 243 bit 1 (2^1 = 2) 0 3^2 = 9 skip result 3 bit 0 (2^0 = 1) 1 3^1 = 3 multiply in result 3 3^13 = 6561 · 81 · 3 = 1594323, using 6 multiplications

Read right to left: each box below is the square of the one to its right, and amber bits pick which squares go into the product. The diagram scrolls sideways on a narrow screen.

Mechanics

Peeling and building digits

Invariant for reversing: after k steps, result holds the last k digits of x in reverse order and rest holds the other digits in their original order. Each step moves one digit from the end of rest to the end of result, so after ⌊log10 x⌋ + 1 steps (one per digit) rest is empty and result is the full reversal. Work on abs(x) and put the sign back at the end: Python's % and // round toward minus infinity, so -123 % 10 is 7, not −3, which is a classic surprise for people coming from C or Java.

Checking the bound before crossing it

If the answer must fit in [−231, 231 − 1], the step result * 10 + digit overflows exactly when result > INT_MAX // 10, or when result == INT_MAX // 10 and digit > INT_MAX % 10 (which is 7). Testing this before the multiply is what makes the check meaningful in a fixed-width language, where the overflowed value would already be garbage. The same check, with the negative bound's last digit 8, clamps atoi. Python will happily compute the big number, so on these problems you either check the bound explicitly or compare the final value against the range; the first is the answer an interviewer is looking for.

Reversing only half

Palindrome Number has a neater invariant: keep moving digits from rest to half while rest > half. When the loop stops, half has at least as many digits as rest, so it holds the back half reversed. The number is a palindrome when the halves are equal (even length) or equal after dropping the middle digit with half // 10 (odd length). Only half the digits are ever built, so no overflow is possible. Negative numbers and positive numbers ending in 0 are rejected up front: no palindrome starts with a minus sign or a leading zero.

Square-and-multiply

Write n in binary as a sum of distinct powers of two. Then xn is the product of x2k over the set bits k. The loop keeps power = x2k for the current bit and result = the product of the powers for the bits already read. Each iteration multiplies power into result if the bit is set, squares power, and shifts n right. That is at most 2 log2 n multiplications. A negative n means 1 / x−n, so invert x first and use −n; in a fixed-width language take care that −(−231) does not fit, which is why the exponent is widened before negating.

Multiplying digit strings

For digit strings a (length m) and b (length n), the product has at most m + n digits. Index the result slots 0 to m + n − 1 from the left. Digit a[i] stands for a[i] · 10m−1−i and b[j] for b[j] · 10n−1−j, so their product's units digit belongs at power m + n − 2 − i − j, which is slot i + j + 1, and its tens digit at slot i + j. Processing pairs from the right and adding into those two slots keeps every slot except the leftmost touched one below ten, and the last pair settles slot 0. Strip leading zeros at the end, keeping one zero if the product is zero.

Multiplying 123 by 45 in a five-slot array Six digit products are added into the slots, each with its units digit at slot i plus j plus 1 and its carry at slot i plus j: 3 times 5 is 15 at slot 4, leaving slots 0 0 0 1 5; 3 times 4 is 12 at slot 3, leaving slots 0 0 1 3 5; 2 times 5 is 10 at slot 3, leaving slots 0 0 2 3 5; 2 times 4 is 8 at slot 2, leaving slots 0 1 0 3 5; 1 times 5 is 5 at slot 2, leaving slots 0 1 5 3 5; 1 times 4 is 4 at slot 1, leaving slots 0 5 5 3 5. The final slots read 0 5 5 3 5, so the product is 5535. slot 0 slot 1 slot 2 slot 3 slot 4 a[2] · b[1] = 3 · 5 = 15 → slot 4 0 0 0 1 5 a[2] · b[0] = 3 · 4 = 12 → slot 3 0 0 1 3 5 a[1] · b[1] = 2 · 5 = 10 → slot 3 0 0 2 3 5 a[1] · b[0] = 2 · 4 = 8 → slot 2 0 1 0 3 5 a[0] · b[1] = 1 · 5 = 5 → slot 2 0 1 5 3 5 a[0] · b[0] = 1 · 4 = 4 → slot 1 0 5 5 3 5 123 × 45 = 5535 amber: units slot i+j+1; blue: carry slot i+j

Each row is the slot array after one digit product. Amber is where the product's units digit was added; blue is the slot that received the carry.

Other bases and notations

Converting to base b peels digits with divmod(n, b), most significant last, so reverse at the end. Excel column titles are base 26 with digits A–Z standing for 1–26 and no zero (bijective base 26), so subtract one before each divmod: then the remainder 0 to 25 maps onto A to Z, and Z (26) does not spill into a phantom zero digit. Roman numerals are additive with one rule: a symbol worth less than its right neighbour is subtracted. Greatest Common Divisor of Strings reduces to Euclid on the lengths once you check that the two strings commute, s + t == t + s, which holds exactly when both are repeats of one common block.

Worked example

Reverse Integer, x = −1230. Work on 1230; the bound for a negative result is 2147483648.

StepDigit poppedRestResult beforeSafe?Result after
101230yes0
23120yes3
3213yes32
41032yes321

The trailing zero becomes a leading zero and disappears. Putting the sign back gives -321.

The overflow case, x = 1534236469. The reversal would be 9646324351, which is larger than 2147483647.

StepDigit poppedResult beforeSafe?Result after
839646324yes96463243
9596463243yes964632435
101964632435no—

At the last step the result is 964632435, already above 214748364 = INT_MAX // 10, so multiplying by ten would overflow. The check fires first and the function returns 0.

Palindrome Number, x = 12321.

StepDigit movedRestReversed half
1112321
2212312
3312123

The loop stops when the rest (12) is no longer greater than the half (123). The length is odd, so drop the middle digit: 123 // 10 = 12, which matches, so 12321 is a palindrome. For 1221 the loop stops at rest 12 and half 12 after 2 steps.

Pow(x, n), x = 3, n = 13 (binary 1101).

Bit positionBit32kResult after
0133
1093
2181243
3165611594323

6 multiplications give 313 = 1594323. With a negative exponent the base is inverted first: 2−2 runs the same loop on 0.5 and returns 0.25.

Multiply Strings, "123" × "45". The table repeats the diagram's slot array after each digit product.

ijDigitsProductUnits slotSlots after
213 · 51540 0 0 1 5
203 · 41230 0 1 3 5
112 · 51030 0 2 3 5
102 · 4820 1 0 3 5
011 · 5520 1 5 3 5
001 · 4410 5 5 3 5

Stripping the leading zero gives 5535.

Smaller conversions.

ProblemInputStepsAnswer
Column title701 701 − 1 = 700 = 26 · 26 + 24 → Y; 26 − 1 = 25 = 26 · 0 + 25 → Z ZY
Roman to integerMCMXCIV +1000 -100 +1000 -10 +100 -1 +5 1994
GCD of strings"ABABAB", "ABAB" both orders concatenate to the same string; lengths: gcd(6, 4) → 6 mod 4 = 2; gcd(4, 2) → 4 mod 2 = 0, so keep the first 2 characters "AB"
Plus One[1, 2, 9] and [9, 9] a trailing 9 becomes 0 and carries left; only all nines grow a new digit 1 3 0 and 1 0 0
atoi" -042x7" space space space sign digit digit digit stop -42
atoi, clamped"91283472332" 9 digits, then clamp at 9128347233 2147483647

Implementation

INT_MAX, INT_MIN = 2**31 - 1, -2**31


def reverse(x):
    sign = -1 if x < 0 else 1
    limit = INT_MAX if sign > 0 else -INT_MIN
    rest, result = abs(x), 0
    while rest:
        rest, digit = divmod(rest, 10)
        if result > limit // 10 or (result == limit // 10 and digit > limit % 10):
            return 0                      # checked before the multiply that would overflow
        result = result * 10 + digit
    return sign * result


def my_pow(x, n):
    if n < 0:
        x, n = 1 / x, -n
    result = 1
    while n:
        if n & 1:
            result *= x
        x *= x
        n >>= 1
    return result

Variations and edge cases

def is_palindrome(x):
    if x < 0 or (x % 10 == 0 and x != 0):
        return False
    half = 0
    while x > half:
        x, digit = divmod(x, 10)
        half = half * 10 + digit
    return x == half or x == half // 10


def multiply(a, b):
    slots = [0] * (len(a) + len(b))
    for i in range(len(a) - 1, -1, -1):
        for j in range(len(b) - 1, -1, -1):
            total = int(a[i]) * int(b[j]) + slots[i + j + 1]
            slots[i + j + 1] = total % 10
            slots[i + j] += total // 10
    return "".join(map(str, slots)).lstrip("0") or "0"


def column_title(n):
    letters = []
    while n:
        n, r = divmod(n - 1, 26)          # bijective base 26: digits are 1..26
        letters.append(chr(ord("A") + r))
    return "".join(reversed(letters))


def plus_one(digits):
    for i in range(len(digits) - 1, -1, -1):
        if digits[i] < 9:
            digits[i] += 1
            return digits
        digits[i] = 0
    return [1] + digits
  • Negative numbers in Python. -7 // 2 is −4 and -7 % 2 is 1. Work on abs(x), or use int(a / b) and math.fmod when you need truncation toward zero.
  • The asymmetric range. −231 fits but 231 does not, so the positive and negative bounds have different last digits (7 and 8). Negating INT_MIN overflows in fixed-width languages.
  • Zero. Reversing 0 and multiplying by "0" are the inputs where loops run zero times or strip every digit; return "0" rather than the empty string. A column title for 1 ("A") is the smallest input, with a single pass.
  • Floating point. x ** 0.5 and math.log10 round; use math.isqrt and integer loops when exactness matters, and compare floats with a tolerance.
  • Modular answers. When a problem says "return it modulo 109 + 7", reduce after every addition and multiplication; Python's built-in pow(x, n, m) is square-and-multiply with a modulus.

Complexity and tradeoffs

  • Digit loops (reverse, palindrome, digit sums, base conversion): O(log x) time, one step per digit, and O(1) space. Converting to a string is also O(log x) and is fine unless the problem forbids it.
  • Pow: O(log n) multiplications against n − 1 for the naive loop, which times out at n = 231. The recursive form, xn = (xn/2)2, is the same algorithm with O(log n) stack.
  • Multiply strings: O(m · n) time and O(m + n) space. Karatsuba's divide and conquer is O(n1.585), which is what CPython uses for huge integers, and FFT-based methods (as in GMP) are near-linear for very large operands, but nobody expects them in an interview.
  • gcd: O(log min(a, b)) steps; the worst case is consecutive Fibonacci numbers (Lamé's theorem).
  • When it is the wrong tool. Adding numbers stored as linked lists is the same carry loop but belongs to linked lists. Counting lattice paths is a binomial coefficient, but grid DP is the safer derivation. Repeated digit-square sums that may loop forever, as in Happy Number, are a cycle question for fast and slow pointers.

Problems

Start with Palindrome Number and Plus One to get used to digits and carries, then Reverse Integer for the overflow check and Pow(x, n) for square-and-multiply. Multiply Strings is the one most people need to practise twice: derive the i + j + 1 slot rather than memorizing it.

Easy

  • 9. Palindrome Number Grind 169
    Reverse only the lower half of the digits and compare it with the upper half, which avoids overflow and string conversion.
  • 13. Roman to Integer Grind 169 NeetCode 250
    Scan symbol values left to right and subtract any value that is smaller than the one after it.
  • 66. Plus One NeetCode 150 NeetCode 250
    Add one from the last digit and carry left; only an all-nines number grows by a new leading digit.
  • 168. Excel Sheet Column Title NeetCode 250
    This is base 26 with no zero digit, so subtract one before each modulo and division.
  • 1071. Greatest Common Divisor of Strings NeetCode 250
    A common divisor string exists only if s+t equals t+s, and then its length is the gcd of the two lengths.

Medium

  • 7. Reverse Integer Grind 169 NeetCode 150 NeetCode 250
    Peel digits off with modulo and division, and check against the 32-bit bounds before each multiply-by-ten rather than after.
  • 8. String to Integer (atoi) Grind 169
    Treat parsing as a tiny state machine: skip spaces, read one sign, consume digits, and clamp the moment the value would leave the 32-bit range.
  • 43. Multiply Strings NeetCode 150 NeetCode 250
    Digit i of one number times digit j of the other contributes to position i+j+1 of the product, so accumulate and carry in an array.
  • 50. Pow(x, n) Grind 169 NeetCode 150 NeetCode 250
    Square the base and halve the exponent at each step, handling a negative exponent by inverting the base first.

Also uses this pattern

These problems have a different home page, but this idea is part of their solution.

Connections

Read first

Read next

Square-and-multiply first appeared on this site as a recursion example in recursion and the call stack; the loop here is the same algorithm read bit by bit. Bit manipulation picks up where this page stops, doing arithmetic on the base-two digits directly. Carry propagation reappears in Add Two Numbers, and the "check the bound before you cross it" habit matters again whenever an index or a sum could run past its range.

Check yourself

Why does reversing x = 1534236469 return 0, and at which step is that decided?

The reversal 9646324351 exceeds 231 − 1. At the last step the partial result is 964632435, already greater than INT_MAX // 10 = 214748364, so any further digit would overflow and the check returns 0 before multiplying.

How many multiplications does square-and-multiply use for 313, and why is it at most about 2 log2 n?

6. Each of the log2 n bits costs at most one squaring and at most one multiply into the result.

Why does the units digit of a[i] · b[j] go into slot i + j + 1?

a[i] is worth 10m−1−i and b[j] is worth 10n−1−j, so the product is worth 10m+n−2−i−j. In a slot array of length m + n indexed from the left, slot k holds 10m+n−1−k, so k = i + j + 1.

Why does column 26 come out as Z rather than "A@" or "AZ"?

Subtracting one first gives 25 = 26 · 0 + 25, so the only digit is Z and the quotient is 0. Without the shift, 26 = 26 · 1 + 0 would produce a zero digit that the notation does not have.

References

  • Cormen, Leiserson, Rivest and Stein, Introduction to Algorithms, 3rd edition, section 31.2 (Euclid's algorithm and Lamé's theorem) and section 31.6 (modular exponentiation by repeated squaring).
  • Knuth, D. E., The Art of Computer Programming, volume 2, Seminumerical Algorithms, section 4.3.1 (classical multiple-precision arithmetic) and section 4.6.3 (evaluation of powers).
  • Skiena, S., The Algorithm Design Manual, the catalog's numerical problems: arbitrary-precision arithmetic and factoring.
  • Sedgewick and Wayne, Algorithms, section 1.1, on Euclid's algorithm and integer types and their ranges.
  • Karatsuba, A. and Ofman, Yu., "Multiplication of many-digital numbers by automatic computers", Doklady Akademii Nauk SSSR, 1962, the first sub-quadratic multiplication.
  • The NeetCode roadmap and the Blind 75, Grind 169, NeetCode 150 and NeetCode 250 lists, used as the index of topics and problems.