Reasoning Into CodeArunank Divya Sharan · Python edition← Arunank’s portfolio
Module 05 · Sliding Windows

044. Longest Substring Without Repeats

Foundation · Download this solution · All solutions and shared helpers

Problem

Return the length of the longest contiguous substring with no repeated character. Case and punctuation are significant. Empty input returns zero. Length counts Python string elements.

Examples

Example 1

longest_unique_substring("abcabcbb")
=> 3

The maximum unique window has length three.

Example 2

longest_unique_substring("abba")
=> 2

The final a cannot reopen the old prefix.

Example 3

longest_unique_substring("")
=> 0

There is no nonempty substring.

Reveal the worked solution and reasoning

Why this mechanism works

When the incoming character repeats inside the active window, its previous occurrence must be excluded. Store last-seen indices and jump the left boundary directly past that occurrence. Never move left backward when the previous occurrence lies outside the active window.

State and boundaries

[left,right+1) is duplicate-free after repair. last records the most recent index of each character anywhere in the processed prefix.

Protocol

  1. Read the incoming character's last position.
  2. Raise left to at least one past that position.
  3. Record its new position.
  4. Commit the repaired window length to best.

Visualization

a b b a
0 1 2 3
second b moves left to 2
last a=0 must not move left backward

Python implementation

def longest_unique_substring(text):
    last = {}
    left = best = 0
    for right, char in enumerate(text):
        if char in last:
            left = max(left, last[char] + 1)
        last[char] = right
        best = max(best, right - left + 1)
    return best

Worked execution

Recorded state changes after statements; L numbers refer to the solution file. Long traces are explicitly condensed.

longest_unique_substring("abcabcbb")

L2 longest_unique_substring: last = {}
text='abcabcbb'; last={}

L3 longest_unique_substring: left = best = 0
text='abcabcbb'; last={}; left=0; best=0

L4 longest_unique_substring: for right, char in enumerate(text):
text='abcabcbb'; last={}; left=0; best=0; right=0; char='a'

L7 longest_unique_substring: last[char] = right
text='abcabcbb'; last={'a':0}; left=0; best=0; right=0; char='a'

L8 longest_unique_substring: best = max(best, right - left + 1)
text='abcabcbb'; last={'a':0}; left=0; best=1; right=0; char='a'

L4 longest_unique_substring: for right, char in enumerate(text):
text='abcabcbb'; last={'a':0}; left=0; best=1; right=1; char='b'

L7 longest_unique_substring: last[char] = right
text='abcabcbb'; last={'a':0, 'b':1}; left=0; best=1; right=1; char='b'

L8 longest_unique_substring: best = max(best, right - left + 1)
text='abcabcbb'; last={'a':0, 'b':1}; left=0; best=2; right=1; char='b'

... 14 intermediate state changes omitted ...

L7 longest_unique_substring: last[char] = right
text='abcabcbb'; last={'a':3, 'b':6, 'c':5}; left=5; best=3; right=6; char='b'

L4 longest_unique_substring: for right, char in enumerate(text):
text='abcabcbb'; last={'a':3, 'b':6, 'c':5}; left=5; best=3; right=7; char='b'

L6 longest_unique_substring: left = max(left, last[char] + 1)
text='abcabcbb'; last={'a':3, 'b':6, 'c':5}; left=7; best=3; right=7; char='b'

L7 longest_unique_substring: last[char] = right
text='abcabcbb'; last={'a':3, 'b':7, 'c':5}; left=7; best=3; right=7; char='b'

Result: 3. The maximum unique window has length three.

longest_unique_substring("abba")

L2 longest_unique_substring: last = {}
text='abba'; last={}

L3 longest_unique_substring: left = best = 0
text='abba'; last={}; left=0; best=0

L4 longest_unique_substring: for right, char in enumerate(text):
text='abba'; last={}; left=0; best=0; right=0; char='a'

L7 longest_unique_substring: last[char] = right
text='abba'; last={'a':0}; left=0; best=0; right=0; char='a'

L8 longest_unique_substring: best = max(best, right - left + 1)
text='abba'; last={'a':0}; left=0; best=1; right=0; char='a'

L4 longest_unique_substring: for right, char in enumerate(text):
text='abba'; last={'a':0}; left=0; best=1; right=1; char='b'

L7 longest_unique_substring: last[char] = right
text='abba'; last={'a':0, 'b':1}; left=0; best=1; right=1; char='b'

L8 longest_unique_substring: best = max(best, right - left + 1)
text='abba'; last={'a':0, 'b':1}; left=0; best=2; right=1; char='b'

... 1 intermediate state changes omitted ...

L6 longest_unique_substring: left = max(left, last[char] + 1)
text='abba'; last={'a':0, 'b':1}; left=2; best=2; right=2; char='b'

L7 longest_unique_substring: last[char] = right
text='abba'; last={'a':0, 'b':2}; left=2; best=2; right=2; char='b'

L4 longest_unique_substring: for right, char in enumerate(text):
text='abba'; last={'a':0, 'b':2}; left=2; best=2; right=3; char='a'

L7 longest_unique_substring: last[char] = right
text='abba'; last={'a':3, 'b':2}; left=2; best=2; right=3; char='a'

Result: 2. The final a cannot reopen the old prefix.

Why the solution is correct

Before insertion the window is unique, so the incoming character is the only possible new duplicate. Removing its earlier occurrence and everything before it restores uniqueness with the longest possible suffix. Maximizing these longest valid suffixes covers the optimal substring.

Time and space

O(n) expected time and O(u) auxiliary space.

Common mistakes

Assigning left=last[char]+1 without max can move backward in abba. Updating last before querying loses the previous index.

Reasoning card

State: [left,right+1) is duplicate-free after repair. last records the most recent index of each character anywhere in the processed prefix.

Invariant: The current window contains no repeated character.

Decision to rehearse: Add right, repair duplicates from left, then commit.

Finish: Commit the repaired window length to best

Transfer exercise

Return the earliest longest substring itself while retaining a strict best-length improvement rule.