Minimum Window Substring

The problem description can be found in LeetCode.

Approach

From the title we already know that we have a Sliding Window problem. In particular, this is a Sliding Window with a Dynamic Size problem.

In fact, the problem doesn't mention any static size to check, so we don't know about the actual size of the window.

The trick here is to find an efficient way to understand if the property of the window is fulfilled. In this case, we need to be sure that all the characters of t are in s (duplicates included).

An efficient way to do so is to keep track of the matching characters. Hence, we will try to shrink the window as soon as we have the same number of matching characters.

This value is simply equal to the size of the counter dictionary that we will create for the string t.

Solution

from collections import defaultdict
from typing import DefaultDict


class Solution:
    def minWindow(self, s: str, t: str) -> str:
        if len(t) > len(s):
            return ""

        t_counter: DefaultDict[str, int] = self.get_chars_counter(t)
        required_chars = len(t_counter)

        left = 0
        matching_chars = 0
        s_counter: DefaultDict[str, int] = defaultdict(int)
        ans = ""

        for right, char in enumerate(s):
            # the current char is not in `t`
            # hence, we can avoid to consider it
            if char not in t_counter:
                continue

            s_counter[char] += 1
            # incrementing the `matching_chars` once the number of occurrences
            # for `char` is equal in both counters
            if s_counter[char] == t_counter[char]:
                matching_chars += 1

            # shrink if we have the same number of matching characters
            while left <= right and matching_chars == required_chars:
                left_char = s[left]
                win_size = right - left + 1
                # update the solution
                if not ans or win_size < len(ans):
                    ans = s[left : right + 1]

                # be sure that the `left_char` is in the `t` string,
                # otherwise we risk to consider unused chars
                if left_char in t_counter:
                    s_counter[left_char] -= 1
                    # decrease the `matching_chars` once `s_counter`
                    # does not contain enough for matching `t`
                    if s_counter[left_char] < t_counter[left_char]:
                        matching_chars -= 1

                left += 1

        return ans

    def get_chars_counter(self, s: str) -> DefaultDict[str, int]:
        counter: DefaultDict[str, int] = defaultdict(int)

        for char in s:
            counter[char] += 1

        return counter

Complexity

  • Time:
  • Space:

Where and are the size of the strings and , respectively. Regarding the space, the alphabet is limited to 56 characters, therefore it is constant.