每日一练:最小覆盖字符串

76. 最小覆盖子串 - 力扣(LeetCode)

一、题目要求

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

注意:

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

示例 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 <= 105
  • s 和 t 由英文字母组成

二、解法1-暴力破解 O(N^2) 会超时

class Solution {
public:
    string minWindow(string s, string t) {
        set<pair<int, string>> set; // 存储所有覆盖到的字符串
        vector<int> tab(123-65, 0); // 需要的字符种类和数量
        int differ = t.size(); // 标识是否都覆盖到了
        for (const auto e : t) {
            tab[e - 'A']--;
        }

        for (int i = 0; i < s.size(); i++) {
            if (tab[s[i] - 'A'] < 0) { // 最短的字串的首个字母肯定是在t中也有的
                string ret;
                int tmp_differ = differ;
                vector<int> tmp_tab(tab);
                for (int j = i; j < s.size(); j++) {
                    ret.push_back(s[j]);
                    if (tmp_tab[s[j] - 'A'] < 0) {
                        tmp_tab[s[j] - 'A']++;
                        tmp_differ--;
                        if (tmp_differ == 0) { // 全部覆盖到了
                            set.insert({ret.size(), ret});
                            break;
                        }
                    }
                }
            }
        }

        if (set.empty())
            return string();
        return set.begin()->second;
    }
};

三、解法2-哈希表+滑动窗口 O(N) 执行时间少,内存消耗高

        用一个哈希表存储需要的字符,differ维护当前窗口缺少的字符的种类与数量

(1)若窗口不满足要求,就让窗口的右边界右移,吸收更多字符,直到满足要求为止,然后执行(2)。

(2)若窗口满足要求,就让窗口的左边界右移,逐个减少不需要的字符,当将要减少的字符会让窗口不满足条件时,就计算当前长度(可能要更新字串),减少这个字符后窗口就不满足要求了,又执行(1)。

class Solution {
public:
    string minWindow(string s, string t) {
        int differ = t.size();
        unordered_map<char, int> map; // 需要的字符种类和数量
        for (const auto c : t) {
            map[c]--;
        }
        for (int i = 0; i < t.size(); i++) {
            auto it = map.find(s[i]);
            if (it != map.end()) {
                if (it->second < 0)
                    differ--;
                map[s[i]]++;
            }
        }
        if(differ == 0) // 如果已经符合条件了,它的大小正好是t的大小,一定是最小的
            return s.substr(0,t.size());
        string ret;
        int j = t.size(); // 将进入到值
        int i = 0; // 将去掉的值
        int min = -1; // 判断目前的字符串是不是最短的
        while(i < s.size())
        {
            // differ==0,i向右走,否则j向右走
            if(differ == 0)
            {
                auto it = map.find(s[i]);
                if(it!=map.end()) // s[i]是map中有的字符
                {
                    if(it->second > 0) // 有多余的s[i],differ不需要增加
                    {
                        (it->second)--;
                    }
                    else // 没有多余的s[i],differ需要增加,记录当前的长度
                    {
                        differ++;
                        (it->second)--;
                        if(min==-1 || j-i<min) // 获得第一个合法长度或者小于最小长度
                        {
                            min = j-i;
                            ret = s.substr(i,min); // 更新最小子串
                        }
                    }
                }
                i++; // i向右走
            }
            else // 窗口不符合,j向右走
            {
                auto it = map.find(s[j]);
                if(it!=map.end()) // s[j]是map中有的字符
                {
                    if((it->second) < 0) // 正好缺少s[j],differ才减少
                    {
                        differ--;
                    }
                    (it->second)++;
                }
                j++; // j向右走
            }
            if(j >= s.size() && differ != 0) // 此时无论i如何移动,都不可能符合条件了
            {
                break;
            }
        }
        return ret;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值