# The Cipher Wheel

> Every letter has an alias - you just need the right codebook.

Canonical URL: <https://datadriven.io/problems/the_cipher_wheel>

Domain: Python · Difficulty: easy · Seniority: L3

## Problem

Given a string s and a dict mapping single characters to single characters, return a new string where each character is replaced by mapping[char] if present, else kept unchanged.

## Worked solution and explanation

### Why this problem exists in real interviews

This tests **dict lookup for character substitution**, a pattern that appears in data masking, encoding, and ETL pipelines. It probes whether a candidate handles the "not found" case cleanly using `.get()` with a default.

---

### Break down the requirements

#### Step 1: Iterate through each character of the input string

Process characters one at a time, building the result sequentially.

#### Step 2: Look up each character in the mapping dict

Use `mapping.get(ch, ch)` to substitute mapped characters while preserving unmapped ones.

#### Step 3: Build and return the result string

Accumulate characters into a result variable and return it.

---

### The solution

**Dict-based character substitution**

```python
def substitute(s: str, mapping: dict) -> str:
    result = ""
    for ch in s:
        result += mapping.get(ch, ch)
    return result
```

> **Time and Space Complexity**
>
> **Time:** O(n) where n is the length of the string. Each dict lookup is O(1) average.
> 
> **Space:** O(n) for the output string.

> **Interviewers Watch For**
>
> Using `dict.get(key, default)` instead of checking `if key in dict` separately. The one-liner `.get(ch, ch)` is the idiomatic pattern and signals fluency with Python dicts.

> **Common Pitfall**
>
> Mutating the original string or forgetting that strings are immutable in Python. You must build a new string.

---

## Common follow-up questions

- What if the mapping should be applied case-insensitively? _(Tests whether you normalize the key before lookup while preserving original casing in unmapped chars.)_
- How would you reverse the substitution? _(Tests inverting a dict, which requires handling potential duplicate values.)_
- What if the input were a very large file? _(Tests streaming approach: process line by line or chunk by chunk instead of loading everything.)_

## Related

- [All practice problems](https://datadriven.io/problems)
- [Mock interview mode](https://datadriven.io/interview/the_cipher_wheel)
- [Python Interview Questions](https://datadriven.io/python-interview-questions)
- [Data Engineering Interview Prep Guide](https://datadriven.io/data-engineer-interview-prep)
- [Daily Challenge](https://datadriven.io/daily)

---

Source: DataDriven (https://datadriven.io). 100% free data engineering interview prep. Live code execution against Postgres 16, Python 3.11, and Spark sandboxes. No paywall, no premium tier, no signup gate.