Sliding Window


TODO’s

Todo

1438. Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit (Medium)

from collections import deque

def longestSubarray(nums: List[int], limit: int) -> int:
   n = len(nums)
   maxDeque, minDeque = deque(), deque()  # Deques to store indices of max and min elements
   left = right = 0
   maxi = 0

   while right < n:
      # Expansion
      while maxDeque and nums[right] > nums[maxDeque[-1]]:
            maxDeque.pop()
      while minDeque and nums[right] < nums[minDeque[-1]]:
            minDeque.pop()
      maxDeque.append(right)
      minDeque.append(right)

      # Check if current window exceeds limit
      while nums[maxDeque[0]] - nums[minDeque[0]] > limit:
            # Shrinking
            if left == maxDeque[0]:
               maxDeque.popleft()
            if left == minDeque[0]:
               minDeque.popleft()
            left += 1
      right += 1

      # Logic
      maxi = max(maxi, right - left)

   return maxi

395. Longest Substring with At Least K Repeating Characters (Medium)

def longestSubstring(self, s: str, k: int) -> int:
   def atMostKUniqueChars(maxUnique):
         n = len(s)
         left = right = 0
         counter = collections.Counter()
         uniqueChars = 0  # Number of unique characters in the current window
         countAtLeastK = 0  # Number of characters that appear at least k times in the current window
         longest = 0

         while right < n:
            # Expansion
            if counter[s[right]] == 0:
               uniqueChars += 1
            counter[s[right]] += 1
            if counter[s[right]] == k:
               countAtLeastK += 1
            right += 1

            # Shrinking
            while left < right and uniqueChars > maxUnique:
               if counter[s[left]] == k:
                     countAtLeastK -= 1
               counter[s[left]] -= 1
               if counter[s[left]] == 0:
                     uniqueChars -= 1
               left += 1

            # Logic
            if uniqueChars == countAtLeastK:
               longest = max(longest, right - left)

         return longest

   maxUniqueChars = len(set(s))  # Maximum possible unique characters in s
   maxi = 0
   for maxUnique in range(1, maxUniqueChars + 1):
         maxi = max(maxi, atMostKUniqueChars(maxUnique))

   return maxi

1156. Swap For Longest Repeated Character Substring (Medium)

def maxRepOpt1(self, text: str) -> int:
   counter = collections.Counter(text)
   left = right = 0
   n = len(text)
   maxi = 0
   freqCounter = collections.defaultdict(int)

   # Iterate through the string with a sliding window
   while right < n:
         # Expand the window
         freqCounter[text[right]] += 1
         right += 1

         # Check if window is valid - contains more than one type of character more than once
         while (right - left) - max(freqCounter.values()) > 1:
            # Shrink the window from the left
            freqCounter[text[left]] -= 1
            left += 1

         # Update maxi to the max length of the window considering all instances of the character in text
         # It considers swapping one character if it increases the window size and if another instance of the character exists outside the current window
         maxi = max(maxi, min((right - left), counter[text[right-1]]))

   return maxi

Tips

All sliding problem will have similar code pattern, with:
  • Expansion

  • Shrinking

  • Problem Logic

But, the problem logic determines which data structure to use:
  • Unique elements = set or dict

  • Frequencies = dict

  • Sum/Average = prefix sum or maintaining total

  • Max/Min element = deque

Visualize me!

digraph G { node [shape=box, style="filled", fillcolor="white"]; // Default node style edge [color="black", fontcolor="black"]; // Default edge style rankdir=LR; // Main categories with unique styles "Sliding Window" [shape=ellipse, style="filled", fillcolor="gray", fontcolor="white", fontsize=16]; "Fixed Window" [shape=ellipse, style="filled", fillcolor="lightblue", fontsize=14]; "Longest Sub" [shape=ellipse, style="filled", fillcolor="lightgreen", fontsize=14]; "Shortest Sub" [shape=ellipse, style="filled", fillcolor="lightpink", fontsize=14]; "Return Count,Indices" [shape=ellipse, style="filled", fillcolor="yellow", fontsize=14]; // Connect main categories to "Sliding Window" "Sliding Window" -> {"Fixed Window" "Longest Sub" "Shortest Sub" "Return Count,Indices"}; // "Fixed Window" subcategories and items "Fixed Window" -> "FW NaïveSumAverage"; "FW NaïveSumAverage" [label="Naïve Sum | Average", fillcolor="azure"]; "Fixed Window" -> "FW Hashmap"; "FW Hashmap" [label="Hashmap", fillcolor="azure"]; "FW Hashmap" -> {"FW Duplicate" "FW Unique" "FW Anagrams | Permutations"}; "FW Duplicate" [label="Duplicate", fillcolor="azure"]; "FW Unique" [label="Unique", fillcolor="azure"]; "FW Anagrams | Permutations" [label="Anagrams | Permutations", fillcolor="azure"]; "Fixed Window" -> "FW Count"; "FW Count" [label="Count", fillcolor="azure"]; "FW Count" -> {"FW Math" "FW Swaps" "FW Count"}; "FW Math" [label="Math", fillcolor="azure"]; "FW Swaps" [label="Swaps", fillcolor="azure"]; "Fixed Window" -> "FW Misc"; "FW Misc" [label="Misc", fillcolor="azure"]; "FW Misc" -> {"FW Heap" "FW Sorting" "FW Bit Manipulation"}; "FW Heap" [label="Heap", fillcolor="azure"]; "FW Bit Manipulation" [label="Bit Manipulation", fillcolor="azure"]; "FW Sorting" [label="Sorting", fillcolor="azure"]; // "Longest Sub{string,array}" subcategories and items "Longest Sub" -> "LS Longest Consecutive X"; "LS Longest Consecutive X" [label="Longest Consecutive X", fillcolor="honeydew"]; "LS Longest Consecutive X" -> {"1 Pass" ">1 Pass"}; "1 Pass" [label="1 Pass", fillcolor="honeydew"]; ">1 Pass" [label=">1 Pass", fillcolor="honeydew"]; "Longest Sub" -> "LS Hashmap"; "LS Hashmap" [label="Hashmap", fillcolor="honeydew"]; "LS Hashmap" -> {"LS Unique" "LS K Distinct"}; "LS Unique" [label="Unique", fillcolor="honeydew"]; "LS K Distinct" [label="K Distinct", fillcolor="honeydew"]; "Longest Sub" -> "LS Misc"; "LS Misc" [label="Misc", fillcolor="honeydew"]; "LS Misc" -> "LS Depends on prev"; "LS Depends on prev" [label="Depends on prev", fillcolor="honeydew"]; // "Shortest Sub{string,array}" subcategories and items "Shortest Sub" -> {"SS Naive" "SS Two Strings"}; "SS Naive" [label="Naive", fillcolor="lavenderblush"]; "SS Two Strings" [label="Two Strings", fillcolor="lavenderblush"]; // "Return Count,Indices" subcategories and items "Return Count,Indices" -> "RCI Naïve Sum | Average"; "RCI Naïve Sum | Average" [label="Naïve Sum | Average", fillcolor="lightyellow"]; "Return Count,Indices" -> "RCI Longest Sub"; "RCI Longest Sub" [label="Longest Sub", fillcolor="lightyellow"]; "RCI Longest Sub" -> "LS Depends on prev"; "Return Count,Indices" -> "RCI At Most K"; "RCI At Most K" [label="At Most K", fillcolor="lightyellow"]; "RCI At Most K" -> {"RCI Less Than K" "RCI Exactly K"}; "RCI Less Than K" [label="Less Than K", fillcolor="lightyellow"]; "RCI Exactly K" [label="Exactly K", fillcolor="lightyellow"]; }