【LeetCode刷题】最小覆盖字串

给定两个字符串 s 和 t,长度分别是 m 和 n,返回 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 <=
  • s 和 t 由英文字母组成

解法思路

  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),适用于英文字符串场景。测试用例验证了算法的正确性,包括基础场景、重复字符及无解情况。

相关推荐
小oo呆4 小时前
【学习心得】Python多线程
python
happylifetree8 小时前
Python70:核心语法-函数进阶-案例2
python
王中阳Go8 小时前
两个入口答案不一致先别调模型:6 行入口口径对账表(附脚本)
人工智能·后端·python·大语言模型·1024程序员节
BK贝壳8 小时前
bool-hybrid-array 落盘我只认 .bha:用 Create_BHA / Ask_BHA 做布尔数组序列化
数据结构·python·numpy·1024程序员节
Sakura_j_h_9 小时前
Python 在定时任务里输出 emoji,UnicodeEncodeError 从哪来
开发语言·字符编码·windows·python·定时任务
supabc12310 小时前
欧盟电子发票(XRechnung/Peppol/ZUGFeRD)踩坑记:用 Python 不装 Java 跑官方校验规则
xml·python·开源·电子发票·1024程序员节
ZhengEnCi11 小时前
AI Agent工具调用防护层怎么写-一份能直接跑的Python四层拦截实现
python·llm·agent
企业数字化笔记11 小时前
视频人体姿态怎么做三维可视化?2D关键点、深度局限与技术选型
人工智能·算法·音视频
ZhengEnCi11 小时前
AI Agent工具误调用怎么解决-从参数校验到高危二次确认的四层工程防线
python·llm·agent
?abc!11 小时前
(数组)581. 最短无序连续子数组
数据结构·算法·leetcode