Photo by Pankaj Patel on Unsplash
171. LeetCode Challenge Solution (Problem 171)
Solution to the LeetCode Challenge 171 Excel Sheet Column Number with, correct code, detailed explanation and walkthrough using Python.
Final Solution
def titleToNumber(self, columnTitle):
"""
:type columnTitle: str
:rtype: int
"""
val = 0
alphabet = "!ABCDEFGHIJKLMNOPQRSTUVWXYZ"
for i,letter in enumerate(columnTitle[::-1]):
val += (26**i) * alphabet.index(letter)
return val
Approach and walkthrough
To work out our solution, we could take a bottom-up approach. That would mean we build a solution for a specific subset of the problem and then proceed to make it more and more general till we arrive at a solution that solves all cases.
Step 1: Single-letter columns
Let's first tackle single-letter column names. They will range from A-Z, with A having the value of 1 and Z having the value 26. This helps us form a neat little sequence, so let's visualise what that would look like.
Column Title | Value |
A | 1 |
B | 2 |
C | 3 |
D | 4 |
E | 5 |
F | 6 |
G | 7 |
H | 8 |
I | 9 |
J | 10 |
K | 11 |
L | 12 |
M | 13 |
N | 14 |
O | 15 |
P | 16 |
Q | 17 |
R | 18 |
S | 19 |
T | 20 |
U | 21 |
V | 22 |
W | 23 |
X | 24 |
Y | 25 |
Z | 26 |
This feels familiar; it looks like an iteration; each letter represents a value and each number an index. We could simply make this into a list, a set, a tuple or any other iterable and call off the index for any letter whose value we want. I'll go with string because it requires minimal formatting to declare and for all intents and purposes is treated as an iterable by Python.
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
In order to fetch the value of any character within a string, we could use the index()
method of the string class. To get the index of, say, the letter "F" 😉, you pass the letter as an argument to the method, like so: alphabet.index("F")
.
But here's the catch, indexing begins with 0 in Python. So, in that iterable of the alphabets that we created, A is indexed at 0, B at 1, and so on. So when you call, the index method, it is going to return to you a value behind the one we want. You could solve that by adding 1 to the value returned by the index function, but you must remember to do that each time you call the index function. I'd prefer to get the value I want straightaway, so I am going to pad the beginning of the alphabet iterable with an extra character that will never be a part of the column title and thus will never be called, and will offset the indices of the subsequent letters by 1. This is why I declare the alphabet iterable as:
alphabet = "!ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Sweet! And elegant as well, in my opinion. But feel free to go the other way, should you prefer otherwise. On to Step 2.
Step-2: The double-letters
Before we dive into forming a solution for double-letter titles, let's take a look at the values of the double-letters and see if we can find a pattern.
Column Title | Value |
Y | 25 |
Z | 26 |
AA | 27 |
AB | 28 |
AC | 29 |
... | ... |
AZ | 52 |
Not to sound too Dora the Explorer, but do you see a pattern? Let's break it down into a conjunction of constituent letters.
Column Title | Value | Components |
AA | 27 | 26 + 1 |
AB | 28 | 26 + 2 |
AC | 29 | 26 + 3 |
... | ... | ... |
AZ | 52 | 26 + 26 |
All the columns between AA and AZ are 26 + the value of the final letter of the column title. Okay, but why 26 (and not 42, cos we all love 42)? It can't be arbitrary. Let's look at the next set of letters to find out. We've already established that the value of the last letter from the right is to be added to get the final value, what remains to be seen is whether there is a pattern in the first letter.
Column Title | Value | Components | Breakdown | Simplified |
BA | 53 | 52 + 1 | (26+26) + 1 | (26*2) + 1 |
BB | 54 | 52 + 2 | (26+26) + 2 | (26*2) + 2 |
BC | 55 | 52 + 3 | (26+26) + 3 | (26*2) + 3 |
BD | 56 | 52 + 4 | (26+26) + 4 | (26*2) + 4 |
... | ... | ... | ... | |
BY | 77 | 52 + 25 | (26+26) + 25 | (26*2) + 25 |
BZ | 78 | 52 + 26 | (26+26) + 26 | (26*2) + 26 |
A pattern emerges, the value contribution of the first letter is its position in the alphabet times 26. You can verify this by checking the values for CA through CZ yourself! Let's note down mathematically what we have gotten so far.
$$Value = (26 \times Index\ of\ 1^{st}\ Letter) + Index\ of\ 2^{nd}\ Letter$$
We're close, but still not at the end of the road. Let's go one step further to get a blanket solution that fulfils all cases.
Step-3: The three words letters
It would be easy if we could plug in the values for ZZ into our previous formula, and then add 1 to get the value of AAA. From there, we could try to infer a pattern.
$$\begin{flalign} Value\ of\ ZZ = (26 \times Position\ of\ Z) + Position\ of\ Z\\\\ Value\ of\ ZZ = (26 \times 26) + 26\\\\ Value\ of\ ZZ = 26^{2} + 26\\\\ Value\ of\ ZZ = 702 \end{flalign}$$
We're not as interested in the number as we are in the formula that we obtained in the equation. Let's now build our table on that basis.
Column Title | Components | Rewritten | Title Subscripts |
AAA | \(26^2 + 26 + 1\) | \((26^2\times1) + (26^1\times1) + (26^0\times1)\) | \(A_2A_1A_0\) |
AAB | \(26^2 + 26 + 2\) | \((26^2\times1) + (26^1\times1) + (26^0\times2)\) | \(A_2A_1B_0\) |
AAC | \(26^2 + 26 + 3\) | \((26^2\times1) + (26^1\times1) + (26^0\times3)\) | \(A_2A_1C_0\) |
AAD | \(26^2 + 26 + 4\) | \((26^2\times1) + (26^1\times1) + (26^0\times4)\) | \(A_2A_1D_0\) |
... | ... | ... | ... |
AAY | \(26^2 + 26 + 25\) | \((26^2\times1) + (26^1\times1) + (26^0\times25)\) | \(A_2A_1Y_0\) |
AAZ | \(26^2 + 26 + 26\) | \((26^2\times1) + (26^1\times1) + (26^0\times26)\) | \(A_2A_1Z_0\) |
Et voilà ! We've successfully derived a generic formula, engineering the value of each letter to its position in the column title as well as its index in the alphabet. There remains no stone left to be unturned.
Step-4: Generalise
The value of every \(n^{th}\) letter of the title corresponds to \(26^n\) times the index of the letter in the alphabet. Thereon, it is the sum of all letters that make up the title, something we can loop across. Let's write down the mathematical formula for it then.
$$Value\ of\ n^{th}\ letter\ of\ Column = 26^n \times Position\ of\ letter$$
This is a simple formula, easy to code as it only uses 2 operators, viz. exponent and multiplication. So let's get to it!
Step-5: Putting it all together in code
Here's what we do, we loop across all letters from the right to the left (this is very important, check how we have subscripted the letters in Step 3), calculate the value of that letter positionally and then add it to our total.
We have to traverse the Column title in reverse order. There are several ways to reverse a string in Python, my favourite is slicing! Here's a short code to show how it is done.
string = "abc"
rev_string = string[::-1]
Explanation — The first colon tells Python which slice of the string to pick. If you provide indices there, it would only pick the letters between those indices. The figure after the second colon is the 'step', in other words, how big a leap should Python take while picking elements from the string. Specifying 2 will give alternate letters, 3 will fetch every 3rd letter. -1 makes it go backwards!
While looping, we're interested in both the letter and its position in the column title, here's where the function enumerate()
comes in handy. It'll pack both of them into a tuple! Our loop thus becomes (remember that we're traversing the title in reverse order):
for i,letter in enumerate(columnTitle[::-1]):
pass # insert the logic here
Now inside the loop we simply calculate the value of the letter with the position we're at using the formula we derived.
for i,letter in enumerate(columnTitle[::-1]):
val = (26**i) * (position_of_letter)
We've calculated the value of each letter individually, and now we have to add all the values
val = 0
for i,letter in enumerate(columnTitle[::-1]):
val = val + ((26**i) * (position_of_letter))
We're calculating the position of the letter with the index of the alphabet iterable. And, whenever we add a value to itself, we can use the +=
operator. Thus, here's the final code:
def titleToNumber(self, columnTitle):
"""
:type columnTitle: str
:rtype: int
"""
val = 0
alphabet = "!ABCDEFGHIJKLMNOPQRSTUVWXYZ"
for i,letter in enumerate(columnTitle[::-1]):
val += (26**i) * alphabet.index(letter)
return val
I hope this made this walkthrough made it clear for you! Stay tuned for more!