Skip to content
← All posts

Excel Sheet Column Title: Base-26 Conversion

5 min read
leetcodeproblemeasymathstrings

Excel Sheet Column Title is LeetCode 168. Given a positive integer columnNumber, return the corresponding Excel column title as a string.

The mapping starts at 1:

  • 1 returns "A", 2 returns "B", ..., 26 returns "Z"
  • 27 returns "AA", 28 returns "AB", ..., 52 returns "AZ"
  • 701 returns "ZY"

This looks like a base-26 conversion, and it almost is. The twist is that normal base-26 uses digits 0 through 25, but Excel columns use A through Z where A represents 1, not 0. That off-by-one changes the algorithm.

Single-letter columns (1 to 26)1A2B3C......26ZMulti-letter columns (27+)27AA28AB701ZY
Columns 1 through 26 map to A through Z. After Z, columns wrap into two-letter titles: 27 is AA, 28 is AB, and so on. 701 maps to ZY.

The approach

In standard base conversion, you repeatedly take n % base to get the least significant digit, then divide n by the base. For base 26 with digits 0 to 25, that works directly.

Excel columns are 1-indexed. A is 1, not 0. If you try 701 % 26 you get 25, which you might map to Z. But then 701 // 26 = 26, and 26 % 26 = 0. There is no letter for 0 in this system, so the standard approach breaks.

The fix is to subtract 1 before each mod operation. This shifts the range from 1-26 down to 0-25, which aligns with standard base conversion where A = 0, B = 1, ..., Z = 25.

The loop:

  1. While n > 0, subtract 1 from n
  2. Take n % 26 to get a value from 0 to 25
  3. Map that value to a character: 0 is 'A', 1 is 'B', ..., 25 is 'Z'
  4. Prepend (or collect and reverse) that character
  5. Set n = n // 26

The subtraction of 1 in each iteration is the entire trick. Without it, column 26 (Z) and column 52 (AZ) produce wrong results because the mod gives 0 when it should not.

Visual walkthrough

Here is the loop traced on columnNumber = 701, which should produce "ZY". Watch how the subtract-1 step shifts each value into the correct 0-to-25 range.

Tracing convertToTitle(701):

Step 1: Convert 701. Start with n = 701.

n =701
n - 1 =701 - 1 = 700
remainder = 700 % 26 =24->'Y'
n = 700 // 26 =700 // 26 = 26
result (reversed so far) ="Y"

Subtract 1 first to make it 0-indexed. 700 % 26 = 24, which maps to 'Y' (A=0, B=1, ..., Y=24). Then n becomes 700 // 26 = 26.

Step 2: n = 26. Still greater than 0, so loop continues.

n =26
n - 1 =26 - 1 = 25
remainder = 25 % 26 =25->'Z'
n = 25 // 26 =25 // 26 = 0
result (reversed so far) ="YZ"

Subtract 1 to get 25. 25 % 26 = 25, which maps to 'Z'. Then n becomes 25 // 26 = 0. The loop will end.

n = 0, loop ends. Reverse "YZ" to get "ZY".

Characters were collected right-to-left (least significant first), so reversing gives the final answer: "ZY".

In the first iteration, subtracting 1 turns 701 into 700. Then 700 % 26 = 24, which maps to 'Y'. In the second iteration, n = 26 becomes 25 after subtracting 1. Then 25 % 26 = 25, which maps to 'Z'. The characters are collected in reverse order (Y then Z), so you reverse them to get "ZY".

The solution

def convertToTitle(columnNumber: int) -> str:
    result = []
    n = columnNumber
    while n > 0:
        n -= 1
        result.append(chr(n % 26 + ord('A')))
        n //= 26
    return ''.join(reversed(result))

The chr(n % 26 + ord('A')) expression converts a number in the range 0 to 25 into the corresponding uppercase letter. ord('A') is 65, so 0 + 65 = 65 which is 'A', 25 + 65 = 90 which is 'Z'.

The result is built in reverse order (least significant letter first), so a final reversed() call puts the letters in the correct left-to-right order.

The n -= 1 before the mod is the only difference between this and a standard base-26 conversion. If you forget it, the algorithm fails on any multiple of 26 (like Z = 26, AZ = 52, ZZ = 702). When drilling this problem, the subtract-1 step is the one detail you must internalize.

Complexity analysis

Time: O(log n) where n is the column number. Each iteration divides n by 26, so the loop runs floor(log26(n)) + 1 times. For n = 701, that is 2 iterations.

Space: O(1) ignoring the output string. The result list holds at most log26(n) + 1 characters, but this is the required output, not extra space. Only a few integer variables are used otherwise.

AspectValue
TimeO(log n)
SpaceO(1)
DifficultyEasy

Edge cases

  • columnNumber = 1 returns "A". The simplest case. n becomes 0 after subtracting 1, 0 % 26 = 0 maps to 'A', then 0 // 26 = 0 ends the loop.
  • columnNumber = 26 returns "Z". This is the case that breaks without the subtract-1 fix. With it: n = 25, 25 % 26 = 25 maps to 'Z', then 25 // 26 = 0.
  • columnNumber = 27 returns "AA". The first two-letter column. First iteration: n = 26, 26 % 26 = 0 maps to 'A', n = 26 // 26 = 1. Second iteration: n = 0, 0 % 26 = 0 maps to 'A', n = 0 // 26 = 0. Reversed: "AA".
  • columnNumber = 702 returns "ZZ". First: n = 701, 701 % 26 = 25 maps to 'Z', n = 701 // 26 = 26. Second: n = 25, 25 % 26 = 25 maps to 'Z', n = 25 // 26 = 0. Reversed: "ZZ".

Building blocks

This problem is built from one core building block.

Modified base conversion

Standard base conversion takes n % base and n // base in a loop. When the digit set starts at 0, this works out of the box. When the digit set starts at 1 (as with A = 1), you subtract 1 from n before each mod to realign the range.

This same "subtract to fix the indexing" idea appears whenever you need to convert between 1-indexed and 0-indexed representations. In Excel columns specifically, the subtraction turns a 1-to-26 range into a 0-to-25 range, making it compatible with the standard % 26 operation.

The underlying digit extraction loop (% base to get the digit, // base to remove it) is the same loop used in Reverse Integer, Palindrome Number, and any problem that processes a number digit by digit. The only variation here is the base (26 instead of 10) and the index shift.

From understanding to recall

The solution is five lines of code. The loop structure is identical to any base conversion. The only non-obvious part is the n -= 1 before the mod, and that single line is the difference between a correct solution and one that fails on multiples of 26.

When you drill this problem, focus on that one detail. If you can remember "subtract 1, then mod 26, then divide 26," you can reconstruct the entire solution. The chr and ord calls are standard Python for letter mapping. The reverse at the end is the same pattern as building any number or string from least significant to most significant.

Related posts

  • Excel Sheet Column Number - The inverse problem: given a column title like "ZY", compute the column number 701. Uses the same base-26 math in the opposite direction.
  • Reverse Integer - Uses the same mod/divide loop to extract digits, just in base 10 instead of base 26.