【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 分钟前
Spring 6.0+Boot 3.0实战避坑全指南:5大类高频问题与解决方案(附代码示例)
java·数据仓库·hive·hadoop·python·技术文档编写
傻啦嘿哟2 分钟前
Python自动整理音乐文件:按艺术家和专辑分类歌曲
数据库·python·分类
denggun123453 分钟前
悬垂指针 和 野指针
数据结构
weixin_462446237 分钟前
基于 Flask + lunar-python 的农历转换 API 实战(公历 ↔ 农历 / 干支 / 生肖 / 节日)
python·flask·节日
Pluto_CSND17 分钟前
JSONPath解析JSON数据结构
java·数据结构·json
无限进步_18 分钟前
【C语言】用队列实现栈:数据结构转换的巧妙设计
c语言·开发语言·数据结构·c++·链表·visual studio
Xの哲學18 分钟前
从硬中断到 softirq:Linux 软中断机制的全景解剖
linux·服务器·网络·算法·边缘计算
weixin_5795996620 分钟前
编写一个程序,输入两个数字的加减乘除余数(Python版)
开发语言·python
liu****23 分钟前
02_Pandas_数据结构
数据结构·python·pandas·python基础
生信碱移27 分钟前
单细胞空转CNV分析工具:比 inferCNV 快10倍?!兼容单细胞与空转的 CNV 分析与聚类,竟然还支持肿瘤的亚克隆树构建!
算法·机器学习·数据挖掘·数据分析·聚类