LeetCode 76:最小覆盖子串

137 阅读3分钟

题目

image.png

思路

  • 初始化变量

    • startminLen 用于记录最小覆盖子串的起始位置和长度。初始长度设置为 s.length() + 1,确保在找到更短的子串时会被更新。
    • leftright 是滑动窗口的左右边界指针。
    • um 是一个哈希表,用于记录字符串 t 中每个字符的出现频次。
    • tlen 用于记录 t 中还有几个字符没有被当前窗口包含。
  • 构建哈希表

    • 遍历字符串 t,将每个字符及其出现频次存入哈希表 um
  • 移动右边界

    • 使用 while 循环移动 right 指针,遍历字符串 s
    • 每当遇到一个字符 s[right],将其在哈希表中的频次减 1。如果该字符是 t 中的字符(即减 1 后仍大于或等于 0),则将 tlen 减 1。
  • 检查并移动左边界

    • tlen 为 0 时,表示当前窗口包含了 t 中的所有字符。

    • 进入 while 循环,尝试缩小窗口大小:

      • 如果当前窗口长度 right - left 小于 minLen,更新 minLenstart
      • 将左边界字符 s[left] 的频次在哈希表中加 1。如果加 1 后频次大于 0,表示该字符是必须的,移出窗口后 tlen 增加 1。
      • left 指针右移,继续尝试缩小窗口。
  • 返回结果

    • 如果找到的最小子串长度小于等于 s 的长度,则返回该子串。
    • 否则,返回空字符串。

代码实现

class Solution 
{
public:
    std::string minWindow(std::string s, std::string t) 
    {
        int start = 0, minLen = s.length() + 1;    // 子串的初始位置及其长度,最长即t的长度
        int left = 0, right = 0;

        // 哈希表,存放 t 中每个字符的出现频次
        std::unordered_map<char, int> um;       
        for (const auto& c : t)
            um[c]++;

        int tlen = t.length();                  // 记录t中还有几个字符没有被两个指针内的串包含
        while (right < s.length())
        {
            // 将 s 中 right 所在位置的字符加入哈希表 um,并将其值减 1
            // 如果减 1 之后其值还大于 0,则该字符为 t 中包含的字符
            // 若 unorder_map 中的值类型为 int,则其默认构造值为 0
            if (um[s[right]]-- > 0)
                tlen--;                         // 被包含,则 tlen 减 1

            right++;

            while (tlen == 0)                   // 两指针内的串包含了 t 中所有字符
            {
                if (minLen > right - left)      // 当前目标串的长度较小,更新结果值
                {
                    start = left;
                    minLen = right - left;
                }

                // 开始移动 left 指针,寻找最短的目标串
                // 窗口将最左边元素相对应的哈希表的值加 1,因为该元素存入哈希表时,其值减了 1
                um[s[left]]++;        

                // 大于 0 表示 left 所在的元素是一个必须元素,将其排除则窗口中的串不包含 t 中所有元素
                // 必须元素是指当前串中该元素的出现频次与 t 中对应元素的频次相同
                // 遍历 s 时将每个元素的哈希值减了 1,如果元素是必须元素,则当 tlen = 0 时,其哈希值应为 0
                if (um[s[left]] > 0)            
                    tlen++;                     // 未被包含的字符数量加 1

                left++;                         // 移动左指针
            }
        }

        if (minLen <= s.length())
            return s.substr(start, minLen);

        return "";
    }
};