Hackbright Code Challenges

Encipher a String: Solution

Encipher a String: Solution

Problem

Encipher a String

Whiteboard

Medium

Challenge

Medium

Concepts

Strings, General

Download

encipher-string-solution.zip


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 az:

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 0len(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)

Manipulating ASCII

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 az map to the values 6597 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)