Strings, General
The simplest solution is to create a lookup table that’ll let us use an original character as a key to get the value of its shifted character.
You can generate a lookup table using two parallel lists — one that contains the characters of the original alphabet and one that contains the characters of the shifted alphabet. Creating the regular list of characters is easy; we can just create a list of characters from a–z:
alpha = list('abcdefghijklmnopqrstuvwxyz')
Now to create the shifted alphabet. Let’s start thinking about what to do in
the simplest case, where we don’t have to shift any letters. In that case,
we could populate an array of shifted characters by looping over a range of
indices from 0–len(alpha)
:
shifted_alpha = []
for i in range(0, len(alpha)):
shifted_alpha.append(alpha[i])
To shift the range, all we have to do is add shift to the start and end of the range:
shifted_alpha = []
for i in range(0 + shift, len(alpha) + shift):
shifted_alpha.append(alpha[i])
One problem though — the code above will cause Python to throw an
IndexError once we get to i = 26
because there is no character at
alpha[26]
! We actually want 26
to be 0
, 27
to be 1
, etc.
To do this, we can take advantage of the mod operator to cause numbers >=
26
to loop back to 0
:
shifted_alpha = []
for i in range(0 + shift, len(alpha) + shift):
shifted_alpha.append(alpha[(i % 26)])
Then returning the encoded string is a matter of creating the lookup table and then looping over all the characters, translating the ones that are alphabet letters:
def rot_encode2(shift, txt):
"""An alternative solution that doesn't involve manipulating ASCII."""
alpha = list('abcdefghijklmnopqrstuvwxyz')
shifted_alpha = []
for i in range(0 + shift, len(alpha) + shift):
shifted_alpha.append(alpha[(i % 26)])
lookup_tbl = {}
for i in range(len(alpha)):
lookup_tbl[alpha[i]] = shifted_alpha[i]
encoded_txt = []
for c in txt:
if c.isalpha():
encoded_c = lookup_tbl[c.lower()]
if c.isupper():
encoded_c = encoded_c.upper()
encoded_txt.append(encoded_c)
else:
encoded_txt.append(c)
return ''.join(encoded_txt)
There’s another solution to this problem that requires very little extra space because we don’t need to create a lookup table. Instead, we manipulate a character’s ASCII value to get the shifted character.
Computers don’t store characters as letters — computers can only store numbers
(binary numbers to be exact). ASCII is a standard character-encoding scheme
that maps characters to decimal numbers. To us, 'a'
looks like the letter
a, but to a computer, it’s actually the integer 65
. The characters
a–z map to the values 65
–97
in ASCII.
Here’s the solution:
def rot_encode(shift, txt):
"""Encode `txt` by shifting its characters to the right."""
# START SOLUTION
encoded_txt = []
for c in txt:
if c.isalpha():
# Convert `c` to its ascii value
ascii_c = ord(c.lower())
# Shift the ascii value. Then scale the resulting
# number so it falls within a range of 0--26 (exclusive).
# Then, mod 26 so values >= 26 loop back to 0. Add 97 to
# turn the number back into valid ascii before converting
# ascii number to a string
encoded_c = chr(((ascii_c + shift - 97) % 26) + 97)
if c.isupper():
encoded_c = encoded_c.upper()
encoded_txt.append(encoded_c)
else:
encoded_txt.append(c)
return ''.join(encoded_txt)