GoCalf Calf 的自留地 · 昙花一现

76. Minimum Window Substring

Problem

Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every character in t (including duplicates) is included in the window. If there is no such substring, return the empty string "".

A substring is a contiguous non-empty sequence of characters within a string.

The testcases will be generated such that the answer is unique.

https://leetcode.com/problems/minimum-window-substring/

Example 1:

Input: s = "ADOBECODEBANC", t = "ABC"
Output: "BANC"
Explanation: The minimum window substring "BANC" includes ‘A’, ‘B’, and ‘C’ from string t.

Example 2:

Input: s = "a", t = "a"
Output: "a"
Explanation: The entire string s is the minimum window.

Example 3:

Input: s = "a", t = "aa"
Output: ""
Explanation: Both ‘a’s from t must be included in the window.
Since the largest window of s only has one ‘a’, return empty string.

Constraints:

  • m == s.length
  • n == t.length
  • 1 <= m, n <= 10⁵
  • s and t consist of uppercase and lowercase English letters.

Test Cases

python
class Solution:
    def minWindow(self, s: str, t: str) -> str:
solution_test.py下载完整源码
py
import pytest

from solution import Solution
from solution2 import Solution as Solution2


@pytest.mark.parametrize('s, t, expected', [
    ("ADOBECODEBANC", "ABC", "BANC"),
    ("a", "a", "a"),
    ("a", "aa", ""),
])
class Test:
    def test_solution(self, s, t, expected):
        sol = Solution()
        assert sol.minWindow(s, t) == expected

    def test_solution2(self, s, t, expected):
        sol = Solution2()
        assert sol.minWindow(s, t) == expected

Thoughts

设当前窗口的左右边界分别是 i 和 j,如果当前子串已经包含 t 中所有字符则右移 i,否则右移 j。

事先统计出 t 中出现的所有字符及数量,在移动 i、j 的时候同步更新子串中的字符种类及数量。

设 t 中有 k 种不同的字符,则时间复杂度是 O(n+km),空间复杂度 O(k)。

当 k 比较大时,每移动一次 i 或 j 都比一次窗口内各种字符的数量与 t 是否一致就比较浪费,比如移入或移出的字符并不在 t 内,或者移出了一个数量已经超出需求很多的字符,或者移入了一个需要的字符但还有很多其他也需要的字符。要把每次移动 i 或 j 时的处理时间降到 O(1)。

可以再维护一个字符的集合,表示窗口内缺少的字符。在移动 i、j 并更新窗口内字符数量时,直接判定当前字符是否还需要,并相应地加入或移出集合,这样每次判定就只需要常数时间。

如果控制好字符加入和移出集合的时机,确保不会重复加入和移出不存在的字符,那就不需要真的放一个集合,只需要记录还缺少的字符数量,可以省掉集合的存储空间和相关操作的时长。

整体的时间复杂度是 O(n+m),空间复杂度 O(k)。

Code

👇 单一循环体

py
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        m = len(s)
        if m < len(t):
            return ''

        needs: dict[str, int] = {} # character: number of this character needed.
        for c in t:
            needs[c] = needs.get(c, 0) + 1
        miss = len(needs)

        min_i = min_len = 0
        i = j = 0
        while not (miss and j == m):
            if miss:
                if (c := s[j]) in needs:
                    needs[c] -= 1
                    if needs[c] == 0:
                        miss -= 1
                j += 1
            else:
                if (c := s[i]) in needs:
                    needs[c] += 1
                    if needs[c] == 1:
                        miss += 1
                        if (curr_len := j - i) < min_len or min_len == 0:
                            min_i = i
                            min_len = curr_len
                i += 1

        return s[min_i:min_i+min_len]

👇 循环套循环

solution2.py下载完整源码
py
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        m = len(s)
        if m < len(t):
            return ''

        needs: dict[str, int] = {} # character: number of this character needed.
        for c in t:
            needs[c] = needs.get(c, 0) + 1
        miss = len(needs)

        min_i = min_len = 0
        i = j = 0
        while True:
            while miss and j < m:
                if (c := s[j]) in needs:
                    needs[c] -= 1
                    if needs[c] == 0:
                        miss -= 1
                j += 1

            if miss:
                break

            while not miss:
                if (c := s[i]) in needs:
                    needs[c] += 1
                    if needs[c] == 1:
                        miss += 1
                i += 1

            if (curr_len := j - i + 1) < min_len or min_len == 0:
                min_len = curr_len
                min_i = i - 1

        return s[min_i:min_i+min_len]

评论需要 JavaScript。