1、解题思路

  1. 滑动窗口法:使用双指针(left 和 right)表示当前窗口的左右边界。扩展 right 指针,直到窗口包含 t 中的所有字符。收缩 left 指针,尽可能缩小窗口,同时保持窗口包含 t 中的所有字符。记录满足条件的最短子串。
  2. 哈希表辅助:使用哈希表记录 t 中每个字符的出现次数。在滑动窗口过程中,维护另一个哈希表,记录当前窗口中各字符的出现次数。判断当前窗口是否满足包含 t 中所有字符的条件。

2、代码实现

C++
#include <climits>
#include <unordered_map>
class Solution {
  public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param S string字符串
     * @param T string字符串
     * @return string字符串
     */
    string minWindow(string S, string T) {
        // write code here
        unordered_map<char, int> target;    // 统计 T 中各字符出现的次数
        for (char c : T) {
            target[c]++;
        }

        int left = 0, right = 0;
        int min_len = INT_MAX;
        int min_left = 0;
        int required = target.size();   // 需要满足的字符种类
        int formed = 0; // 当前窗口中满足的字符种类数
        unordered_map<char, int> window;    // 统计当前窗口中各字符的出现次数

        while (right < S.size()) {
            char c = S[right];
            window[c]++;
            if (target.count(c) && window[c] == target[c]) {
                formed++;
            }

            // 尝试收缩窗口
            while (left <= right && formed == required) {
                // 更新最小窗口
                if (right - left + 1 < min_len) {
                    min_len = right - left + 1;
                    min_left = left;
                }

                // 移动左边界
                char left_char = S[left];
                window[left_char]--;
                if (target.count(left_char) && window[left_char] < target[left_char]) {
                    formed--;
                }
                left++;
            }
            right++;
        }
        return min_len == INT_MAX ? "" : S.substr(min_left, min_len);
    }
};

Java
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param S string字符串 
     * @param T string字符串 
     * @return string字符串
     */
    public String minWindow (String S, String T) {
        // write code here
        Map<Character, Integer> target = new HashMap<>();
        for (char c : T.toCharArray()) {
            target.put(c, target.getOrDefault(c, 0) + 1);
        }

        int left = 0, right = 0;
        int minLen = Integer.MAX_VALUE;
        int minLeft = 0;
        int required = target.size();
        int formed = 0;
        Map<Character, Integer> window = new HashMap<>();

        while (right < S.length()) {
            char c = S.charAt(right);
            window.put(c, window.getOrDefault(c, 0) + 1);
            if (target.containsKey(c) && window.get(c).equals(target.get(c))) {
                formed++;
            }

            while (left <= right && formed == required) {
                if (right - left + 1 < minLen) {
                    minLen = right - left + 1;
                    minLeft = left;
                }

                char leftChar = S.charAt(left);
                window.put(leftChar, window.get(leftChar) - 1);
                if (target.containsKey(leftChar) && window.get(leftChar) < target.get(leftChar)) {
                    formed--;
                }
                left++;
            }

            right++;
        }

        return minLen == Integer.MAX_VALUE ? "" : S.substring(minLeft, minLeft + minLen);
    }
}

Python
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param S string字符串 
# @param T string字符串 
# @return string字符串
#
from collections import defaultdict

class Solution:
    def minWindow(self , S: str, T: str) -> str:
        # write code here
        target = defaultdict(int)
        for c in T:
            target[c] += 1

        left = 0
        min_len = float('inf')
        min_left = 0
        required = len(target)
        formed = 0
        window = defaultdict(int)

        for right, c in enumerate(S):
            window[c] += 1
            if c in target and window[c] == target[c]:
                formed += 1

            while left <= right and formed == required:
                if right - left + 1 < min_len:
                    min_len = right - left + 1
                    min_left = left

                left_char = S[left]
                window[left_char] -= 1
                if left_char in target and window[left_char] < target[left_char]:
                    formed -= 1
                left += 1

        return "" if min_len == float('inf') else S[min_left:min_left + min_len]

3、复杂度分析

  1. 滑动窗口法: 通过双指针动态调整窗口大小,确保窗口始终包含 t 中的所有字符。
  2. 哈希表辅助: 使用哈希表统计 t 中字符的出现次数,并在滑动窗口中维护当前窗口的字符统计。
  3. 时间复杂度: 滑动窗口遍历字符串 s 一次,时间复杂度为O(n)。
  4. 空间复杂度: 使用哈希表存储字符统计,空间复杂度为O(n)。