【LeetCode刷题】最小覆盖字串

给定两个字符串 st,长度分别是 mn,返回 s 中的 最短窗口 子串 ,使得该子串包含 t 中的每一个字符(包括重复字符 )。如果没有这样的子串,返回空字符串""

测试用例保证答案唯一。

示例 1:

复制代码
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

示例 2:

复制代码
输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。

示例 3:

复制代码
输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

提示:

  • m == s.length
  • n == t.length
  • 1 <= m, n <=
  • st 由英文字母组成

解法思路

  1. 统计目标字符频率 :用字典 need 记录字符串 t 中每个字符的出现次数。
  2. 滑动窗口遍历 :用左右指针维护滑动窗口,右指针先扩大窗口,直到窗口包含 t 的所有字符;再移动左指针缩小窗口,寻找最小满足条件的子串。
  3. 验证窗口有效性 :用变量 valid 记录窗口中满足 need 要求的字符种类数,当 valid 等于 need 的长度时,窗口满足条件。
  4. 记录最小窗口:在窗口满足条件时,持续更新最小窗口的起始位置和长度。

Python 代码:

python 复制代码
from collections import defaultdict


class Solution:
    def minWindow(self, s: str, t: str) -> str:
        """
        寻找字符串s中包含字符串t所有字符的最小覆盖子串
        :param s: 源字符串
        :param t: 目标字符串(需要包含的所有字符)
        :return: 满足条件的最小子串,无则返回空字符串
        """
        # 边界条件1:目标字符串t为空,直接返回空
        if not t:
            return ""
        # 边界条件2:源字符串s比t短,不可能包含,返回空
        if len(s) < len(t):
            return ""

        # 统计t中各字符的需求次数(key:字符,value:需要的次数)
        need = defaultdict(int)
        for c in t:
            need[c] += 1

        # 滑动窗口内的字符统计(记录窗口中各字符的出现次数)
        window = defaultdict(int)
        left = right = valid = 0  # valid:窗口中满足"次数达标"的字符种类数
        start = 0  # 最小窗口的起始索引(最终结果的起始位置)
        min_len = float('inf')  # 最小窗口的长度(初始化为无穷大)

        # 右指针遍历,扩大窗口
        while right < len(s):
            # 取出当前右指针指向的字符,然后右指针右移
            current_char = s[right]
            right += 1

            # 若当前字符是t需要的,更新窗口内的统计
            if current_char in need:
                window[current_char] += 1
                # 当窗口内该字符的数量 == 需求数量时,该字符达标,valid+1
                if window[current_char] == need[current_char]:
                    valid += 1

            # 当窗口满足所有字符需求(valid等于需要的字符种类数),尝试缩小左窗口
            while valid == len(need):
                # 更新最小窗口的信息(如果当前窗口更短)
                current_window_len = right - left
                if current_window_len < min_len:
                    start = left  # 记录最小窗口的起始位置
                    min_len = current_window_len  # 更新最小长度

                # 取出左指针指向的字符,准备移出窗口
                remove_char = s[left]
                left += 1  # 左指针右移,缩小窗口

                # 若移出的字符是t需要的,更新窗口统计和valid
                if remove_char in need:
                    # 若移出前该字符刚好达标,移出后不再达标,valid-1
                    if window[remove_char] == need[remove_char]:
                        valid -= 1
                    # 窗口内该字符的计数-1
                    window[remove_char] -= 1

        # 最终判断:若min_len仍为无穷大,说明无满足条件的窗口,否则返回子串
        return "" if min_len == float('inf') else s[start:start + min_len]


# ------------------- 测试用例 -------------------
if __name__ == "__main__":
    solution = Solution()

    # 测试用例1:基础场景
    s1 = "ADOBECODEBANC"
    t1 = "ABC"
    print(f"测试用例1结果:{solution.minWindow(s1, t1)}")  # 预期输出:"BANC"

    # 测试用例2:t包含重复字符
    s2 = "a"
    t2 = "a"
    print(f"测试用例2结果:{solution.minWindow(s2, t2)}")  # 预期输出:"a"

    # 测试用例3:无满足条件的子串
    s3 = "a"
    t3 = "aa"
    print(f"测试用例3结果:{solution.minWindow(s3, t3)}")  # 预期输出:""

    # 测试用例4:t为空
    s4 = "abc"
    t4 = ""
    print(f"测试用例4结果:{solution.minWindow(s4, t4)}")  # 预期输出:""

    # 测试用例5:s和t完全一致
    s5 = "abcdef"
    t5 = "abcdef"
    print(f"测试用例5结果:{solution.minWindow(s5, t5)}")  # 预期输出:"abcdef"

LeetCode提交代码:

python 复制代码
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        from collections import defaultdict
        # 统计t中各字符的需求次数
        need = defaultdict(int)
        for c in t:
            need[c] += 1
        
        # 滑动窗口内的字符统计
        window = defaultdict(int)
        left = right = valid = 0  # valid:满足需求的字符种类数
        start = 0  # 最小窗口的起始索引
        min_len = float('inf')  # 最小窗口的长度
        
        while right < len(s):
            # 右指针右移,扩大窗口
            c = s[right]
            right += 1
            
            # 若当前字符是t需要的,更新窗口统计
            if c in need:
                window[c] += 1
                # 当窗口内该字符的数量满足需求时,valid+1
                if window[c] == need[c]:
                    valid += 1
            
            # 当窗口满足所有字符需求时,尝试左移窗口缩小范围
            while valid == len(need):
                # 更新最小窗口的信息
                current_len = right - left
                if current_len < min_len:
                    start = left
                    min_len = current_len
                
                # 左指针右移,缩小窗口
                d = s[left]
                left += 1
                
                # 若移出的字符是t需要的,更新窗口统计和valid
                if d in need:
                    # 若移出前该字符数量刚好满足需求,移出后不再满足,valid-1
                    if window[d] == need[d]:
                        valid -= 1
                    window[d] -= 1
        
        # 若未找到满足条件的窗口,返回空字符串;否则返回最小子串
        return "" if min_len == float('inf') else s[start:start+min_len]

程序运行截图展示:

总结

题目要求从字符串s中找出包含字符串t所有字符(包括重复字符)的最短子串。采用滑动窗口算法,通过双指针动态调整窗口范围:右指针扩展窗口直至满足条件,左指针收缩窗口以寻找最小解。使用哈希表统计字符频率,并通过valid变量验证窗口有效性。若存在满足条件的子串,返回最小窗口对应的子串;否则返回空字符串。时间复杂度为O(m+n),适用于英文字符串场景。测试用例验证了算法的正确性,包括基础场景、重复字符及无解情况。

相关推荐
皇族崛起2 小时前
【3D标注】- Unreal Engine 5.7 与 Python 交互基础
python·3d·ue5
你想知道什么?2 小时前
Python基础篇(上) 学习笔记
笔记·python·学习
monster000w2 小时前
大模型微调过程
人工智能·深度学习·算法·计算机视觉·信息与通信
小小晓.2 小时前
Pinely Round 4 (Div. 1 + Div. 2)
c++·算法
SHOJYS2 小时前
学习离线处理 [CSP-J 2022 山东] 部署
数据结构·c++·学习·算法
biter down3 小时前
c++:两种建堆方式的时间复杂度深度解析
算法
zhishidi3 小时前
推荐算法优缺点及通俗解读
算法·机器学习·推荐算法
WineMonk3 小时前
WPF 力导引算法实现图布局
算法·wpf
Swizard3 小时前
速度与激情:Android Python + CameraX 零拷贝实时推理指南
android·python·ai·移动开发
一直跑3 小时前
Liunx服务器centos7离线升级内核(Liunx服务器centos7.9离线/升级系统内核)
python