intermediate7 min read·Updated September 19, 2026
Longest Substring Without Repeating Characters Explained: pwwkew & abba
Master the sliding window pattern for the longest substring without repeating characters. Follow a step-by-step trace of pwwkew and the tricky abba backward trap.
By Learnisim AI·Published September 19, 2026
LEARNING ARCSetup → Model in the example → Full trace → Twist → Edge cases → Apply
PREREQUISITES
- short strings
Why
Why We Need a Smarter Way to Scan Strings
Phase 1: The Scanning Trap
Imagine you are given the string and asked to find the longest contiguous section of characters where no letter repeats. If you try the most obvious brute-force approach, you would list every possible substring, check each one for duplicates, and pick the longest. For short strings that sounds harmless, but as the length grows, checking every pair of start and end indices explodes into or even operations.
Now consider a slightly more devious input, . When scanning from left to right, your intuition might tell you to just start over or jump backward whenever you hit a duplicate character. But as we will see later, naive backward jumps cause you to miscalculate overlapping character windows and ruin your result.
We need a way to slide across the string in a single forward pass, dynamically adjusting our window boundaries without re-scanning characters we have already validated.
Model
Sliding Window and Char-Map Model for String Scanning
Phase 2: The Sliding Window Model
To find the longest substring without repeating characters in strings like
"pwwkew" and "abba", we stop restarting from scratch at every index. We keep one window that is unique right now, and we slide it forward.The window has two moving edges:
- right walks the string once, from index 0 to . Each step tries to include .
- left only moves forward. When is a duplicate that still sits inside the window, we jump left to one past that character's last index.
- right walks the string once, from index 0 to . Each step tries to include .
- left only moves forward. When is a duplicate that still sits inside the window, we jump left to one past that character's last index.
A hash map stores each character's last seen index. On each right:
1. Look up , the last index of , if any.
2. If exists and , the duplicate is still in the window, so set . Never move left backward — that is the
3. Record in the map.
4. Update .
1. Look up , the last index of , if any.
2. If exists and , the duplicate is still in the window, so set . Never move left backward — that is the
"abba" trap.3. Record in the map.
4. Update .
On
"pwwkew" the window grows to "wke" or "kew" (length 3). On "abba", after the two s, left is already at the second ; the final must not pull left back to 0, so the answer stays 2, not 3.Worked example
Tracing the Sliding Window Algorithm on pwwkew and abba
Phase 3: Step-by-Step Trace of the Sliding Window
To see how the left and right pointers interact with our character map, let's trace our two canonical test strings:
s = "pwwkew" and s = "abba". We maintain a map storing the most recent index of each character, a left pointer starting at index 0, and a max_len accumulator.Trace 1: s = "pwwkew"
- right = 0, char =
- right = 1, char =
- right = 2, char =
- right = 3, char =
- right = 4, char =
- right = 5, char =
p: map = {"p": 0}. left = 0. Window = "p" (len 1). max_len = 1.- right = 1, char =
w: map = {"p": 0, "w": 1}. left = 0. Window = "pw" (len 2). max_len = 2.- right = 2, char =
w: w is in map at index 1, and (). We jump left to . map = {"p": 0, "w": 2}. Window = "w" (len 1). max_len = 2.- right = 3, char =
k: map = {"p": 0, "w": 2, "k": 3}. left = 2. Window = "wk" (len 2). max_len = 2.- right = 4, char =
e: map = {"p": 0, "w": 2, "k": 3, "e": 4}. left = 2. Window = "wke" (len 3). max_len = 3.- right = 5, char =
w: w is in map at index 2, and (). We jump left to . map = {"p": 0, "w": 5, "k": 3, "e": 4}. Window = "kew" (len 3). max_len = 3.Trace 2: s = "abba" (The Backward-Left Trap)
- right = 0, char =
- right = 1, char =
- right = 2, char =
- right = 3, char =
a: map = {"a": 0}, left = 0, max_len = 1.- right = 1, char =
b: map = {"a": 0, "b": 1}, left = 0, max_len = 2.- right = 2, char =
b: b is at index 1 (). left jumps to . map = {"a": 0, "b": 2}, left = 2. Window = "b" (len 1).- right = 3, char =
a: a is in the map at index 0. However, index 0 is strictly less than current left (). Therefore, we ignore it and do not move left backward. left remains 2. Window = "ba" (len 2). max_len = 2.Result for
"pwwkew" is 3, and for "abba" is 2.Practice
Predicting the Window Slide on a Tricky Repeating Sequence
Phase 4: Practice
Now that you have seen how the left pointer leaps forward to rather than incrementing by one, it is time to test your mental model. Consider the string which we analyzed in the previous phase. Imagine the scanning window has just processed the first two characters (indices 0 and 1) and then encountered the first at index 2.
Before you look at the trace or write code for it, reason through what happens when the right pointer reaches the final character at index 3. Recall the core danger we established: the character was already seen at index 0, but our left pointer was already shifted past index 0 when we processed the duplicate s.
Work through the following question to verify your understanding of how the character-index map prevents illegal backward slides.
Apply
Transferring the Sliding Window to Similar Substring Constraints
Phase 5: Applying the Pattern to New Substring Constraints
Now that you have traced how the sliding window and character index map process both and , you can transfer this exact mechanism to neighboring string problems. The core engine we built relies on a single right pointer expanding the boundary, a left pointer that only moves forward when a duplicate is found at or to the right of , and an integer map storing the last seen indices. This pattern reappears whenever you need to maintain a valid window state under character constraints.
Suppose you encounter a modified problem: finding the longest substring that contains at most two distinct characters, or finding the longest substring with repeating characters allowed up to replacements. While those problems require minor adjustments—such as keeping a frequency count instead of a last-seen index—the bounding logic remains identical. When designing an algorithmic solution for these variations, always check if your window contraction logic safely prevents the left pointer from retreating, preserving the strict invariant we established with .
FAQ
Why does the algorithm fail on 'abba' if we move the left pointer backward?
When you encounter the second 'a', if you move the left pointer back to where the first 'a' was, it incorrectly includes two 'b's in the window. The left pointer must only move forward to max(current_left, last_seen_index + 1).
What is the time and space complexity of the sliding window approach?
The time complexity is O(n) because both the left and right pointers traverse the string at most once. The space complexity is O(min(n, m)), where m is the size of the character charset stored in the hash map.
When should I use a sliding window instead of nested loops?
Use a sliding window when dealing with contiguous subarrays or substrings where a constraint (like uniqueness or sum) can be maintained dynamically by expanding and contracting the window edges.