leetcode_76_最小覆盖子串

最小覆盖子串

描述

困难

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

示例

输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"

说明

如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。

解题

采用类似滑动窗口的想法

首先定义一个字典,用来保存t中的字符及对应个数

例如t=‘ABC’, t_dict={‘A’:1, ‘B’:1, ‘C’:1}

在s中定义一个滑动窗口,左右指针为left和right

定义最小的子串窗口,左右指针为min_left和min_right

当s的滑动窗口中出现了t中的字符时,t_dict中对应value减一

当t_dict中所有value不大于0时,说明t的字符全部出现在窗口中

value可以为负,说明在窗口中出现某字符的次数超过了t中的该字符的次数

为了判断t中字符是否已经全部出现,定义t_len

窗口滑动中出现了一个字符,并且在t_dict中的value大于0时,t_len减1

当t_len等于0时,说明t的字符全部出现在窗口中

遍历s时

  • 当t_len > 0时,说明窗口中还没有包含所有t中的字符, right右移来扩大滑动窗口
  • 当t_len == 0时,说明窗口中已包含所有t的字符,但可能同一字符出现了比t中次数还要多的次数,通过left右移来减小滑动窗口
from collections import defaultdict


class Solution:
    def minWindow(self, s: str, t: str) -> str:
        t_dict = defaultdict(int)
        for char in t:
            t_dict[char] += 1

        t_len = len(t)
        left = 0
        min_left = 0
        min_right = len(s)

        for right, char in enumerate(s):  # 这样写的话每次窗口右端right自动右移
            if char in t_dict and t_dict[char] > 0:
                t_len -= 1

            if char in t_dict:
                t_dict[char] -= 1

            # 窗口中已包含所有字符,需要移动left找到最短窗口
            if t_len == 0:
                while True:
                    if s[left] not in t_dict:  # 跳过非t中的字符
                        left += 1
                    elif t_dict[s[left]] < 0:  # 跳过比t出现中更多次的多余字符
                        t_dict[s[left]] += 1
                        left += 1
                    else:
                        break

                # 最小窗口
                if right - left < min_right - min_left:
                    min_left, min_right = left, right
                # 最小窗口的第一个字符肯定是t中的字符,left向后滑动
                t_dict[s[left]] += 1
                t_len += 1
                left += 1

        # 如果min_right没有发生变化,说明没有最小窗口,也就是无解
        return '' if min_right == len(s) else s[min_left:min_right + 1]


if __name__ == "__main__":
    S = "ADOBECODEBANC"
    T = "ABC"
    print(Solution().minWindow(S, T))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值