title: Excel Sheet Column Title slug: excel-sheet-column-title difficulty: easy leetcode_id: 168 leetcode_url: https://leetcode.com/problems/excel-sheet-column-title/ categories: - strings - math patterns: - slug: greedy is_optimal: true function_signature: "def convert_to_title(column_number: int) -> str:" test_cases: visible: - input: { column_number: 1 } expected: "A" - input: { column_number: 28 } expected: "AB" - input: { column_number: 701 } expected: "ZY" hidden: - input: { column_number: 26 } expected: "Z" - input: { column_number: 27 } expected: "AA" - input: { column_number: 52 } expected: "AZ" description: | Given an integer `columnNumber`, return *its corresponding column title as it appears in an Excel sheet*. For example: ``` A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ... ``` constraints: | - `1 <= columnNumber <= 2^31 - 1` examples: - input: "columnNumber = 1" output: '"A"' explanation: "The 1st column in Excel is labeled 'A'." - input: "columnNumber = 28" output: '"AB"' explanation: "After Z (26), we wrap to AA (27), then AB (28)." - input: "columnNumber = 701" output: '"ZY"' explanation: "701 = 26 * 26 + 25 = 676 + 25. The first character is Z (26th letter), and the second is Y (25th letter)." explanation: intuition: | This problem is essentially converting a number to **base-26**, but with an important twist: Excel columns are **1-indexed**, not 0-indexed. In standard base-26 (like hexadecimal is base-16), the digits would be 0-25. But Excel uses A-Z representing 1-26. There's no "zero" character! Think of it like this: imagine you're reading an odometer, but instead of digits 0-9, you have letters A-Z. And unlike a normal odometer where 0 exists, this one starts at A (which represents 1). The key insight is that before extracting each "digit", we need to **subtract 1** to shift from 1-indexed to 0-indexed. This converts our 1-26 range to 0-25, which maps perfectly to A-Z using modulo arithmetic. For example, with `columnNumber = 28`: - Subtract 1 → 27 - 27 % 26 = 1 → maps to 'B' - 27 // 26 = 1 - Subtract 1 → 0 - 0 % 26 = 0 → maps to 'A' - Result: "AB" (reversed from our extraction order) approach: | We solve this using **repeated division with 1-index adjustment**: **Step 1: Initialise result string** - `result`: Empty string to build our column title   **Step 2: Extract characters from right to left** - While `columnNumber > 0`: - **Subtract 1** from `columnNumber` to convert from 1-indexed to 0-indexed - Get the remainder when divided by 26 — this gives us the current character (0 = A, 1 = B, ..., 25 = Z) - Convert the remainder to a character using `chr(remainder + ord('A'))` - **Prepend** this character to the result (or append and reverse at the end) - Divide `columnNumber` by 26 to move to the next position   **Step 3: Return the result** - Return the constructed column title string   The subtraction step is crucial — it handles the fact that there's no "zero" in Excel's numbering. Without it, 'Z' (26) would incorrectly produce 'AZ' instead of just 'Z'. common_pitfalls: - title: Forgetting the 1-Index Adjustment description: | The most common mistake is treating this as standard base-26 conversion without accounting for 1-indexing. For example, without subtracting 1: - `columnNumber = 26` → 26 % 26 = 0, 26 // 26 = 1 → gives "A" + something - But the answer should be just "Z"! The subtraction converts our 1-26 range to 0-25, making the modulo operation work correctly. wrong_approach: "Direct modulo without adjustment" correct_approach: "Subtract 1 before each modulo operation" - title: Building String in Wrong Order description: | When extracting digits via repeated division, we get them in reverse order (rightmost first). A common error is appending characters and forgetting to reverse, or getting confused about which end to add to. For `28`: - First extraction gives 'B' (the rightmost character) - Second extraction gives 'A' (the leftmost character) - If we append: "BA" (wrong!) - If we prepend: "AB" (correct!) wrong_approach: "Appending characters without reversing" correct_approach: "Prepend each character, or append then reverse at the end" - title: Off-By-One with Character Mapping description: | After the modulo operation, the remainder is 0-25. Some implementations incorrectly use `chr(remainder + ord('A') + 1)` or similar, resulting in off-by-one errors. Remember: remainder 0 should map to 'A', remainder 1 to 'B', etc. Since `ord('A')` is 65, we use `chr(remainder + 65)` or `chr(remainder + ord('A'))`. key_takeaways: - "**1-indexed systems require adjustment**: When a numbering system starts at 1 instead of 0, subtract 1 before modulo operations" - "**Base conversion pattern**: This technique of repeated division and modulo extends to any base conversion (binary, hex, etc.)" - "**Build strings carefully**: When extracting digits right-to-left, remember to reverse or prepend" - "**Related problems**: Excel Sheet Column Number (LC 171) is the inverse operation — converting title back to number" time_complexity: "O(log26 n). Each iteration divides the number by 26, so we perform approximately log base 26 of n iterations." space_complexity: "O(log26 n). The output string length is proportional to the number of iterations." solutions: - approach_name: Iterative Division is_optimal: true code: | def convert_to_title(column_number: int) -> str: result = [] while column_number > 0: # Subtract 1 to convert from 1-indexed to 0-indexed column_number -= 1 # Get the current character (0 = A, 1 = B, ..., 25 = Z) remainder = column_number % 26 result.append(chr(remainder + ord('A'))) # Move to the next position column_number //= 26 # We built the string right-to-left, so reverse it return ''.join(reversed(result)) explanation: | **Time Complexity:** O(log26 n) — We divide by 26 each iteration. **Space Complexity:** O(log26 n) — The result list stores one character per iteration. The key insight is subtracting 1 before each modulo operation. This converts from Excel's 1-indexed system (A=1, Z=26) to a 0-indexed system (A=0, Z=25) that works naturally with modulo arithmetic. - approach_name: Recursive Solution is_optimal: false code: | def convert_to_title(column_number: int) -> str: if column_number == 0: return "" # Subtract 1 for 1-indexed adjustment column_number -= 1 # Recursively get the prefix, then append current character return convert_to_title(column_number // 26) + chr(column_number % 26 + ord('A')) explanation: | **Time Complexity:** O(log26 n) — Same number of operations as iterative. **Space Complexity:** O(log26 n) — Call stack depth plus result string. This recursive approach builds the string naturally in the correct order by recursing first (handling higher-order digits) then appending the current character. While elegant, it uses additional stack space compared to the iterative solution.