191 lines
7.5 KiB
YAML
191 lines
7.5 KiB
YAML
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:
|
|
- greedy
|
|
|
|
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(log<sub>26</sub> n). Each iteration divides the number by 26, so we perform approximately log base 26 of n iterations."
|
|
space_complexity: "O(log<sub>26</sub> 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(log<sub>26</sub> n) — We divide by 26 each iteration.
|
|
|
|
**Space Complexity:** O(log<sub>26</sub> 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(log<sub>26</sub> n) — Same number of operations as iterative.
|
|
|
|
**Space Complexity:** O(log<sub>26</sub> 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.
|