Skip to content
← All posts

To Lower Case: ASCII Arithmetic and Bit Manipulation

3 min read
leetcodeproblemeasystrings

LeetCode 709, To Lower Case, asks you to convert every uppercase letter in a string to lowercase. Leave all other characters untouched.

For example:

  • "Hello" returns "hello"
  • "here" returns "here" (already lowercase)
  • "LOVELY" returns "lovely"

The trick is understanding how ASCII encodes uppercase and lowercase letters.

inputoutputH72e101l108l108o111+32h104e101l108l108o111uppercase (A=65)lowercase (a=97)
Converting "Hello" to "hello". Uppercase letters get +32 added to their ASCII value. Non-uppercase characters stay the same.

The ASCII insight

In ASCII, uppercase A through Z occupy codes 65 through 90. Lowercase a through z sit at 97 through 122. The gap between each uppercase letter and its lowercase counterpart is exactly 32. So 'A' (65) becomes 'a' (97) by adding 32, 'B' (66) becomes 'b' (98) by adding 32, and so on.

That constant offset gives you two clean approaches:

  1. Arithmetic: If a character's ASCII value is between 65 and 90, add 32.
  2. Bit manipulation: OR the character with 32. In binary, 32 is 0010 0000. Uppercase letters have bit 5 clear, and lowercase letters have bit 5 set. ORing with 32 flips that single bit without affecting anything else.

Both approaches run in O(n) time with a single pass over the string.

Python's built-in str.lower() does this for you, but interviewers want to see the manual approach. Understanding the ASCII relationship is a building block that appears in many character manipulation problems.

The code

def to_lower_case(s: str) -> str:
    result = []
    for ch in s:
        code = ord(ch)
        if 65 <= code <= 90:
            result.append(chr(code + 32))
        else:
            result.append(ch)
    return "".join(result)

Here is the bit manipulation variant:

def to_lower_case(s: str) -> str:
    result = []
    for ch in s:
        code = ord(ch)
        if 65 <= code <= 90:
            result.append(chr(code | 32))
        else:
            result.append(ch)
    return "".join(result)

Both versions do the same thing. The only difference is + 32 versus | 32. For uppercase letters, the result is identical because bit 5 is always 0 in uppercase ASCII values.

Step-by-step walkthrough

Here is the algorithm converting "Hello" character by character.

Step 1: Process 'H' at index 0

72 + 32 = 104
H72e101l108l108o111

'H' is uppercase (ASCII 72). Add 32 to get 104, which is 'h'. Alternatively, 72 | 32 = 104.

Step 2: Process 'e' at index 1

101 (no change)
h104e101l108l108o111

'e' is already lowercase (ASCII 101). No change needed.

Step 3: Process 'l' at index 2

108 (no change)
h104e101l108l108o111

'l' is already lowercase (ASCII 108). No change needed.

Step 4: Process 'l' at index 3

108 (no change)
h104e101l108l108o111

'l' is already lowercase (ASCII 108). No change needed.

Step 5: Process 'o' at index 4

111 (no change)
h104e101l108l108o111

'o' is already lowercase (ASCII 111). No change needed.

Each step checks whether the character falls in the uppercase range. If it does, the code adds 32 (or sets bit 5). Otherwise it passes the character through unchanged.

Complexity analysis

MetricValue
TimeO(n), one pass over the string
SpaceO(n), for the result array

You visit each character exactly once. The result array holds the same number of characters as the input. If the language allows in-place mutation (like C or Go), you can reduce space to O(1).

Building blocks

This problem teaches two reusable techniques that appear across many string and bit problems.

ASCII range checking

Testing whether a character falls inside a numeric range (like 65 to 90 for uppercase) is a pattern you will use in problems involving digit detection, letter classification, and custom character filters. The idea generalizes: any time you need to categorize characters, their ASCII values give you a clean numeric test.

Bit manipulation for case conversion

ORing with 32 to set bit 5 is a specific instance of using bitwise operations to toggle or set individual properties. You will see similar bit tricks in problems like Detect Capital and single-number XOR problems. The key insight is that a single bit controls the uppercase/lowercase distinction in ASCII.

Edge cases

Already lowercase. The string "abc" has no uppercase letters. Every character fails the range check, so the output is identical to the input.

Non-letter characters. Characters like digits, spaces, and punctuation have ASCII values outside the 65 to 90 range. They pass through untouched. For example, "Hello 123!" becomes "hello 123!".

Empty string. An empty input produces an empty output. The loop body never executes.

Mixed case. The string "HeLLo" converts only the uppercase letters: "hello". Each character is handled independently.

From understanding to recall

You can follow this solution right now and it feels obvious. But will you remember the ASCII offset of 32 two weeks from now? Will the bit manipulation trick come to mind during a timed interview?

Spaced repetition helps you lock in these small, critical details. By drilling the ASCII range check and the +32 offset at increasing intervals, the pattern becomes automatic. When you see a character manipulation problem in an interview, you will not need to re-derive the numbers.

Related posts