Skip to content
Reliable Data Engineering
Practice problem medium sliding-windowhash-mapstrings
Practise with timer, notes and rubric

Longest Substring Without Repeating Characters

Pattern: Sliding Window · Difficulty: Medium · Asked at: Amazon, Meta, Google, Bloomberg

Classic version: LeetCode 3

Problem

Given a string s, return the length of the longest contiguous substring that contains no repeated character.

Examples

longest_unique("abcabcbb")  → 3    # "abc"
longest_unique("bbbbb")     → 1
longest_unique("pwwkew")    → 3    # "wke" ("pwke" is not contiguous)
longest_unique("")          → 0

Constraints

Starter code

def longest_unique(s: str) -> int:
    pass

Hints

Hint 1

Keep a window s[left:right+1] that never contains a duplicate. Grow it one character at a time on the right.

Hint 2

Store the last index where you saw each character. When s[right] was seen inside the window, jump left to one past that index: no need to shrink one step at a time.

Where this shows up in data engineering

The same window shape answers “longest session without a repeated page”, “longest run of distinct device IDs” or “max distinct events in a window” in stream processing. Interviewers like it because the jump-the-left-pointer trick separates people who memorised the template from people who understand the invariant.

Solution

def longest_unique(s: str) -> int:
    last_seen = {}
    left = best = 0
    for right, ch in enumerate(s):
        # only a repeat *inside* the current window forces the left edge to move
        if last_seen.get(ch, -1) >= left:
            left = last_seen[ch] + 1
        last_seen[ch] = right
        best = max(best, right - left + 1)
    return best

Tests

Your solution should pass these:

assert longest_unique("abcabcbb") == 3
assert longest_unique("bbbbb") == 1
assert longest_unique("pwwkew") == 3
assert longest_unique("") == 0
assert longest_unique(" ") == 1
assert longest_unique("abba") == 2
assert longest_unique("dvdf") == 3

Explanation

Invariant: s[left:right+1] has no duplicates. Each step adds s[right]; if that character already occurs inside the window, every window starting at or before its previous position is invalid, so left jumps straight past it.

The classic bug: "abba". When the second a arrives, its last index (0) is left of the window (which already starts at 2 after the second b). Jumping to 0 + 1 would move the window backwards. The >= left check (or left = max(left, last+1)) prevents it.

Complexity: O(n) time, O(min(n, alphabet)) space.

Alternative: a set plus a while loop that removes s[left] until the duplicate is gone. Same O(n) (each character enters and leaves once), easier to adapt to “at most k repeats”.

Follow-up questions

Return the substring itself, not just its length.

Track best_start whenever best improves and return s[best_start:best_start+best].

Allow each character at most twice.

Use a Counter for the window and a while counts[ch] > 2: shrink loop. The jump trick no longer applies because one occurrence leaving doesn’t fix the window on its own.

The input is an unbounded stream of events. How do you report the current longest unique run?

Keep last_seen and left as state, emitting right - left + 1 per event. State grows with the number of distinct keys; bound it with a TTL (drop keys whose last index is left of the window) so memory tracks the window, not the stream.