Recommended resources
The freely available resources this lesson will direct you to.
In our last lesson, we explored the fixed-size sliding window, a powerful technique for efficiently processing contiguous subarrays of a predetermined length. We saw how updating the window's state in constant time led to a significant performance gain. Now, we'll advance this concept to handle a more dynamic and common class of problems.
Today's learning outcome is to implement the expand-and-shrink logic for variable-size sliding windows to meet a condition. Unlike its fixed-size counterpart, this pattern doesn't rely on a given window length k. Instead, the window dynamically grows and contracts to find the optimal subarray—the longest or shortest—that satisfies a specific constraint. This expand-and-shrink mechanism is a cornerstone of efficient algorithm design for many array and string problems you'll encounter in coding interviews.
From Fixed to Variable: The Core Idea
With a fixed-size window, the motion is straightforward: the window slides one position at a time, maintaining its size. But what if a problem asks for the "longest subarray with a sum less than or equal to S" or the "shortest substring containing characters 'A', 'B', and 'C'"? Here, the optimal window size is unknown.
This is where the variable-size window shines. It operates based on a simple but powerful principle:
- Expand: Grow the window by moving its right boundary.
- Check: After each expansion, check if the window's state violates the given condition.
- Shrink: If the condition is violated, shrink the window from its left boundary until it becomes valid again.
This continuous process of expanding and shrinking ensures that we efficiently explore all potential subarrays while maintaining the desired property. The logic is managed by two pointers, typically named left and right (or start and end), which mark the boundaries of our window.
The Expand-and-Shrink Mechanism in Action
Let's make this concrete. The core of the algorithm is a loop structure where the right pointer consistently moves forward, and an inner loop moves the left pointer only when necessary.
-
Expansion Phase: A
forloop iterates therightpointer from the beginning to the end of the array. In each step, we include the elementarray[right]into our window and update the window's state (e.g., add its value to a running sum, or increment its count in a hash map). -
Contraction Phase: Inside the
forloop, awhileloop checks if the window is currently invalid. If it is, we contract the window by moving theleftpointer forward. As we do this, we must also reverse the changes made by the outgoing elementarray[left]to our window's state. Thiswhileloop continues until the window becomes valid again.
After each valid state is achieved (i.e., after the potential shrinking), we can update our answer (e.g., record the window's length if it's the longest we've seen so far).
Let's examine a classic problem that perfectly illustrates this dynamic: "Fruit Into Baskets" (LeetCode 904), which is equivalent to finding the longest subarray with at most two distinct elements.
Variable Length Sliding Window
This article from Hello Interview builds the intuition for the variable-length sliding window. It starts with a brute-force approach and gradually refines it into the optimal solution.
Start by reading the problem description for "Fruit Into Baskets". You can skim the Naive Approach and the first improvement to appreciate why the sliding window is so much more efficient. Then, carefully study the section "Don't Blow It All Up!", which explains the expand-and-shrink logic conceptually. Finally, review the sections on Iteration and Contraction to see how the pointers work together.
As the article explains, the key is to stop "blowing it all up." Instead of restarting our subarray count from scratch, we intelligently shrink the window just enough to restore its validity (containing at most two types of fruit) and then continue expanding.
A General Template for Variable-Size Windows
This "expand-and-shrink" logic can be distilled into a reusable template. Your experience with LeetCode patterns will make this structure feel familiar, and it's a powerful tool to have.
def variable_sliding_window(arr):
left = 0
max_len = 0
# Data structure to track the state of the window (e.g., hash map, set)
window_state = {}
for right in range(len(arr)):
# 1. EXPAND the window by adding the right element
# Update window_state with arr[right]
# 2. SHRINK the window if it's invalid
while condition_is_violated(window_state):
# Update window_state by removing arr[left]
left += 1
# 3. UPDATE the result. At this point, the window is always valid.
max_len = max(max_len, right - left + 1)
return max_len
This template is the blueprint. The main variations lie in the choice of window_state and the logic within condition_is_violated. Let's see it applied to another problem.
Example 2: Longest Substring Without Repeating Characters
This is another quintessential sliding window problem (LeetCode 3). The constraint is simple: the window (substring) must not contain any duplicate characters.
The window_state can be a hash set to keep track of characters currently in the window.
- Expand: Add
s[right]to the set. - Invalid Condition: A duplicate is found, meaning
s[right]was already in the set. - Shrink: Remove
s[left]from the set and incrementleftuntil the duplicates[right]is no longer present in the window.
The animation below shows this process for the string "abcabcbb".

The following video provides a very clear, step-by-step walkthrough of this exact logic.
Sliding Window Algorithm - Variable Length + Fixed Length - DSA Course in Python Lecture 13
This video by Greg Hogg is an excellent, detailed walkthrough of implementing the variable-size sliding window for the "Longest Substring Without Repeating Characters" problem.
Start with the problem setup to understand the goal. Then, pay close attention to the visual explanation from the core logic, where the presenter uses 'L' and 'R' pointers and a set to demonstrate the expand-and-shrink process. Finally, see how this translates into Python code in the implementation section.
Notice the code structure in the video perfectly matches our template: a for loop for the right pointer (expansion) and a while loop for the left pointer (contraction).
Adapting the Pattern: Minimum Window Substring
The expand-and-shrink pattern is versatile. It works for "longest" problems as well as "shortest" problems. Consider LeetCode 76: "Minimum Window Substring." Here, you are given a string S and a string T, and you must find the shortest substring in S that contains all characters of T.
The state management is more complex. You typically need two hash maps:
needs: To count the required characters fromT.window: To count the characters in your current sliding window.
The logic follows the same pattern:
- Expand: Move
rightand update thewindowmap until the characters in your window satisfy theneedsmap. - Shrink: Once you have a valid (but likely non-optimal) window, you shrink from the
leftto find the absolute minimum size that is still valid. Record this size if it's a new minimum. Continue shrinking until the window becomes invalid again, at which point the expansion phase resumes.

We will dive deeper into this specific problem type in a future lesson, but for now, it's useful to see how the core framework adapts.
Mastering Sliding Window: The 'Expand-Contract' Guide for ...
This section from the dev.to article introduces the "Minimum Window Substring" problem. It demonstrates how the same expand-and-shrink framework applies, but with more intricate state management.
Read through the Solution Approach and Implementation. Focus not on memorizing the code, but on understanding how the expand and shrink logic is adapted: the window expands until a condition is met (formed == required_count), and then it shrinks from the left to find the smallest possible valid window.
Common Pitfalls and Complexity
As you've seen in your own practice, implementation details matter. Here are a few things to watch out for with this pattern:
- Window Size Calculation: Be consistent. If your pointers are inclusive, the window size is
right - left + 1. An off-by-one error here is common. - State Updates: When shrinking the window, you must correctly reverse the operation you performed when expanding. If you added to a count, you must subtract. If you deleted from a map's count, be sure to handle the case where the count becomes zero.
- Shrinking Loop: The shrinking logic must be a
whileloop, not anifstatement. A singlerightpointer move might require shrinking theleftpointer multiple times to restore validity. - Time Complexity: You might see the nested
for/whileloops and think the complexity is . However, it is amortized . This is because each pointer,leftandright, only moves forward through the array. Each element is visited at most twice—once by therightpointer and once by theleftpointer. Therefore, the total number of operations is proportional to , which is . - Space Complexity: The space complexity is determined by the data structure used to maintain the
window_state. For problems involving character sets, it can be whereKis the size of the character set (e.g., 26 for lowercase English letters), which is effectively . In the worst case, it could be if all elements in the input are unique and need to be stored.
Conclusion
In this lesson, we've moved beyond fixed-size windows to the more dynamic and powerful variable-size window. You've learned the fundamental expand-and-shrink logic that drives this pattern.
Key Takeaways:
- Variable-size sliding windows are ideal for finding the longest or shortest contiguous subarray that satisfies a given condition.
- The core mechanism is the expand-and-shrink logic, driven by a
rightpointer that always expands and aleftpointer that contracts the window only when it becomes invalid. - A general template using a
forloop for expansion and a nestedwhileloop for contraction can solve a wide variety of these problems. - Despite the nested loops, the time complexity is linear, , because each element is processed by each pointer at most once.
In our next lesson, we will focus on applying this variable-size window pattern to solve a variety of longest-substring problems with different constraints. This will help you solidify your ability to recognize when to use this pattern and how to adapt the window_state and validity conditions for different problem requirements.