leetcode面试经典150题之最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 :

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

初见这道题,我也是没有什么思路的。对于这种找最小的,我想到的是要从最大开始慢慢缩小,最终实现也确实是这样的,但是我没有想到具怎么缩小,缩小到什么指标,所以代码是我看了提示和案例之后分析借鉴写出来的:

#include <iostream>
#include<vector>
#include<string>
#include<unordered_map>
class Solution {
public:
    std::string minWindow(std::string s, std::string t) //s是待处理序列,t是目标序列
    {
        std::unordered_map<char,int> hs, ht;//哈希表hs记录滑动窗口各个字符出现的次数,ht记录目标序列各个字符出现次数
        for (char v : t)//每个字母出现的次数
            ht[v]++;
        std::string ret;
        int cnt = 0;//用于记录窗口中符合t要求的字符个数
        int slen = s.size();
        for (int rp = 0, lp = 0; rp < slen; rp++)//初始化左右窗口边沿
        {
            hs[s[rp]]++;//在哈希表hs中记录右边沿新加入的字符
            if (hs[s[rp]] <= ht[s[rp]])//右边沿收录的字符数量尚不满足所需个数,则将之收录,且cnt++,如果多余则不再记录
            {
                cnt++;
            }
            while (hs[s[lp]]>ht[s[lp]])//当左边沿收录的字符的数量多需要的数量,向右移动左边沿直到数量不再冗余
            {
                hs[s[lp]]--;
                lp++;
            }
            if (cnt == t.size())//如果当前已经包含了所有需要的字符
            {
                if (ret.empty() || rp - lp + 1 < ret.size())//检查是否为空或新的满足更加短
                {
                    ret = s.substr(lp, rp - lp + 1);//将更短的取代作为答案
                }
            }
        }
        return ret;
    }
};

整体的思路是用双指针将部分字符串框出来,向右扩大窗口,并用同时记录每个字母出现的次数。那第一个难点就是:如何便捷地记录每个字母出现的次数?

对于我这种没什么算法基础的人来说,想要记录每个字母出现的次数,自然是用数组来存储,但如此一来,数组的大小就要确定下来,太小,容易塞不下,太大会造成空间浪费。用vector容器吧,就得

std::vector<std::string[2]> Vec;

这样来命名,实在是麻烦,有没有更好的容器呢,自然是有的,就是unordered_map,需要引用头文件

#include<unordered_map>

unordered_map方便的地方就在于可以充当哈希表,非常便捷地记录目标字符串中每个字符出现的次数。

代码的相关注释我已经在代码中标注,下面解释下我当初会有的困惑,说不定也是其他人会有的困惑:为什么cnt只会++不会--?

这个问题可以这么想:什么时候cnt会++?代码中很清晰可以看到,当右边沿收录的字符数量上不满足该字母所需要的个数,cnt会++。比如说题目中的是目标字符是“ABC”那么我收录到“ABBA”时,因为缺少了个C,所以依然cnt会增加。

那么什么时候cnt不增加呢?当左边沿收录到的字符已经满足要求的时候:比如说我们在ABBA的后面紧接着来了一个C,那么在收录到C的时候,我们cnt增加到与t.size()相同,说明我们左右边沿框选住的字符串里已经包含了目标字符串t中的所有字符,这个时候就可以收缩左指针,让我们的长度精简,此时会检查我们框选的最左侧,也就是lp所指向的字符“A”,显然ABBAC中的A数量为2,超过了目标字符“ABC”中的数量1,因此我们将其精简,将lp收缩,框选的字符串变为BBAC,但是此时我们数量依然是满足要求的,所以cnt不会减。

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值