Skip to content

S1Q2 · Encode by Shifting Ends

⚡ Quick Reference

Function: encode_by_shifting_ends(s: str) -> str

Core idea: shift the first character forward and the last backward by 1 in ASCII, with guards for 'z' (first) and 'a' (last).

def encode_by_shifting_ends(s: str) -> str:
    first = s[0] if s[0] == 'z' else chr(ord(s[0]) + 1)
    last  = s[-1] if s[-1] == 'a' else chr(ord(s[-1]) - 1)
    if len(s) == 1:
        return first
    return first + s[1:-1] + last

Key rules: - First char: +1 ASCII (forward), unless it's 'z' → keep unchanged - Last char: -1 ASCII (backward), unless it's 'a' → keep unchanged - Middle characters: unchanged - Length 1: only the first rule applies (first == last, use first's rule)


Problem Statement

Problem

Write a function encode_by_shifting_ends(s) that shifts the first character forward and the last character backward by one ASCII position, keeping the middle unchanged. Exception: 'z' as first or 'a' as last stays unchanged.

Examples:

Input
"abc"
Output
"bbb"
Input
"hello"
Output
"ielln"
Input
"zoo"
Output
"zon"
Input
"a"
Output
"a"
Input
"za"
Output
"za"

Understanding the problem

Three parts of the string get different treatment:

Part Rule
s[0] (first) chr(ord(s[0]) + 1) unless s[0] == 'z'
s[1:-1] (middle) Unchanged
s[-1] (last) chr(ord(s[-1]) - 1) unless s[-1] == 'a'

Edge cases: - Length 1: s[0] and s[-1] point to the same character. Apply only the first-char rule (forward shift / 'z' guard). - Length 2: s[1:-1] is an empty string - no middle.

ord() and chr()

ord('a') = 97, ord('z') = 122. Each letter is one ASCII step apart. chr(ord(c) + 1) moves forward one letter, chr(ord(c) - 1) moves backward. Guards prevent going out of the a-z range.


Tracing all examples

"abc""bbb"

first: 'a' → chr(97+1) = 'b'
middle: (none - only 1 char between ends... wait, len=3)
        s[1:-1] = 'b' → unchanged
last:  'c' → chr(99-1) = 'b'
Result: 'b' + 'b' + 'b' = "bbb" ✓

"hello""ielln"

first: 'h' → chr(104+1) = 'i'
middle: s[1:-1] = 'ell' → unchanged
last:  'o' → chr(111-1) = 'n'
Result: 'i' + 'ell' + 'n' = "ielln" ✓

"zoo""zon"

first: 'z' → 'z' is 'z' → keep 'z'
middle: s[1:-1] = 'o' → unchanged
last:  'o' → chr(111-1) = 'n'
Result: 'z' + 'o' + 'n' = "zon" ✓

"a" (length 1) → "a"

first == last == 'a'
Apply first-char rule: 'a' != 'z' → chr(97+1) = 'b'?

Wait - 'a' as first → shift forward to 'b'. But output is "a".

Re-reading: "a""a". For length 1, the single character is both first and last. The last-char rule says if s[-1] == 'a' keep unchanged. Since 'a' == 'a', keep it. For a single-character string, the last-char rule takes precedence (or both rules conflict and 'a' guard wins).

"za""za"

first: 'z' == 'z' → keep 'z'
last:  'a' == 'a' → keep 'a'
middle: (none)
Result: "za" ✓


Solution approaches

def encode_by_shifting_ends(s: str) -> str:
    first = s[0] if s[0] == 'z' else chr(ord(s[0]) + 1)
    if len(s) == 1:
        # single char: last-char rule ('a' guard) takes precedence
        return s[0] if s[0] == 'a' else first
    last = s[-1] if s[-1] == 'a' else chr(ord(s[-1]) - 1)
    return first + s[1:-1] + last
def encode_by_shifting_ends(s: str) -> str:
    # Encode first character
    if s[0] == 'z':
        first = 'z'
    else:
        first = chr(ord(s[0]) + 1)

    # Single character - apply 'a' guard
    if len(s) == 1:
        return s[0] if s[0] == 'a' else first

    # Encode last character
    if s[-1] == 'a':
        last = 'a'
    else:
        last = chr(ord(s[-1]) - 1)

    # Combine
    middle = s[1:-1]
    return first + middle + last
def encode_by_shifting_ends(s: str) -> str:
    shift_up   = lambda c: c if c == 'z' else chr(ord(c) + 1)
    shift_down = lambda c: c if c == 'a' else chr(ord(c) - 1)

    if len(s) == 1:
        return shift_down(s[0])   # 'a' guard wins for single char
    return shift_up(s[0]) + s[1:-1] + shift_down(s[-1])

Named lambdas for each shift direction - shift_up with 'z' guard, shift_down with 'a' guard. Clean and reusable.


Key takeaways

01

ord() and chr() for ASCII shifting

ord(c) converts a character to its ASCII code; chr(n) converts back. chr(ord(c) + 1) moves one letter forward, chr(ord(c) - 1) moves one letter backward.

02

s[1:-1] for the middle slice

For strings of length 1 or 2, s[1:-1] returns an empty string - concatenating it adds nothing. This means the same formula works for all lengths ≥ 2 without special-casing short strings.

03

Length-1 edge case needs explicit handling

When len(s) == 1, the same character is both first and last. The two rules conflict - the 'a' guard (last-char rule) takes precedence, returning the character unchanged if it's 'a'.