# LeetCode 709: To Lower Case

## Problem Restatement

We are given a string `s`.

Return a new string after replacing every uppercase letter with the same lowercase letter.

All other characters stay unchanged.

The string consists of printable ASCII characters.

## Input and Output

| Item | Meaning |
|---|---|
| Input | A string `s` |
| Output | The same string with uppercase letters converted to lowercase |
| Constraint | `1 <= s.length <= 100` |
| Characters | Printable ASCII characters |

The function shape is:

```python
class Solution:
    def toLowerCase(self, s: str) -> str:
        ...
```

## Examples

Example 1:

```python
s = "Hello"
```

The uppercase letter `H` becomes `h`.

Output:

```python
"hello"
```

Example 2:

```python
s = "here"
```

There are no uppercase letters.

Output:

```python
"here"
```

Example 3:

```python
s = "LOVELY"
```

Every letter is uppercase.

Output:

```python
"lovely"
```

## First Thought: Use the Built-in Method

Python already has a method for this:

```python
s.lower()
```

So the shortest accepted solution is:

```python
class Solution:
    def toLowerCase(self, s: str) -> str:
        return s.lower()
```

This is correct and simple.

But this problem is often used to practice character handling, so we can also implement the conversion manually.

## Key Insight

In ASCII, uppercase letters have consecutive codes:

```python
'A' -> 65
'B' -> 66
...
'Z' -> 90
```

Lowercase letters also have consecutive codes:

```python
'a' -> 97
'b' -> 98
...
'z' -> 122
```

The difference between matching uppercase and lowercase letters is always:

```python
ord('a') - ord('A') == 32
```

So if a character `ch` is between `'A'` and `'Z'`, its lowercase version is:

```python
chr(ord(ch) + 32)
```

Characters outside that uppercase range should be copied unchanged.

## Algorithm

Create an empty list `result`.

For each character `ch` in `s`:

1. If `ch` is between `'A'` and `'Z'`, convert it to lowercase by adding `32` to its ASCII code.
2. Otherwise, keep `ch` unchanged.
3. Append the resulting character to `result`.

At the end, join the list into a string.

## Correctness

The algorithm examines every character of `s`.

If a character is an uppercase ASCII letter, then it lies between `'A'` and `'Z'`. Adding `32` to its ASCII value gives the matching lowercase letter. Therefore the algorithm converts every uppercase letter correctly.

If a character is not an uppercase ASCII letter, the algorithm appends it unchanged. Therefore lowercase letters, digits, spaces, and punctuation are preserved.

Since every character is processed exactly once and appended in the original order, the returned string is exactly `s` with uppercase letters replaced by their lowercase versions.

## Complexity

Let `n` be the length of `s`.

| Metric | Value | Why |
|---|---|---|
| Time | `O(n)` | We scan every character once |
| Space | `O(n)` | We build a result string of length `n` |

## Implementation

```python
class Solution:
    def toLowerCase(self, s: str) -> str:
        result = []

        for ch in s:
            if "A" <= ch <= "Z":
                result.append(chr(ord(ch) + 32))
            else:
                result.append(ch)

        return "".join(result)
```

## Code Explanation

We use a list because strings are immutable in Python:

```python
result = []
```

For each character, check whether it is uppercase ASCII:

```python
if "A" <= ch <= "Z":
```

If so, convert it:

```python
result.append(chr(ord(ch) + 32))
```

If not, keep it unchanged:

```python
else:
    result.append(ch)
```

Finally, join all characters into the answer:

```python
return "".join(result)
```

## Alternative: Bit Operation

ASCII uppercase and lowercase letters differ by one bit.

So another manual conversion is:

```python
class Solution:
    def toLowerCase(self, s: str) -> str:
        result = []

        for ch in s:
            if "A" <= ch <= "Z":
                result.append(chr(ord(ch) | 32))
            else:
                result.append(ch)

        return "".join(result)
```

This works because `ord(ch) | 32` sets the lowercase bit for uppercase ASCII letters.

The range check is still important. Without it, applying `| 32` to punctuation or other characters can change them incorrectly.

## Testing

```python
def test_to_lower_case():
    s = Solution()

    assert s.toLowerCase("Hello") == "hello"
    assert s.toLowerCase("here") == "here"
    assert s.toLowerCase("LOVELY") == "lovely"
    assert s.toLowerCase("A1B2C3") == "a1b2c3"
    assert s.toLowerCase("Python-3.12!") == "python-3.12!"
    assert s.toLowerCase("already lower") == "already lower"

    print("all tests passed")

test_to_lower_case()
```

Test coverage:

| Test | Why |
|---|---|
| Mixed case | Confirms partial conversion |
| Already lowercase | Confirms unchanged letters |
| All uppercase | Confirms full conversion |
| Digits | Confirms nonletters stay unchanged |
| Punctuation | Confirms printable ASCII characters are preserved |

