当前位置: 面试刷题>> 最小覆盖子串(经典算法150题)


### 题目描述 **题目:最小覆盖子串** 给定一个字符串 `s` 和一个字符串 `t`,找出 `s` 中包含 `t` 的所有字符的最小子串。如果 `s` 中不存在这样的子串,则返回空字符串。需要注意的是,`t` 中的字符不需要在 `s` 中以连续的顺序出现,但必须在 `s` 的子串中每个字符至少出现一次,且子串的长度要尽可能小。 **示例 1**: ``` 输入: s = "ADOBECODEBANC", t = "ABC" 输出: "BANC" ``` **示例 2**: ``` 输入: s = "abb", t = "abb" 输出: "abb" ``` **示例 3**: ``` 输入: s = "ab", t = "ab" 输出: "ab" ``` **示例 4**: ``` 输入: s = "axxxxyyyyb", t = "xyy" 输出: "xyyyb" ``` ### 解题思路 为了找到包含 `t` 中所有字符的最小子串,我们可以使用滑动窗口和哈希表的方法。具体步骤如下: 1. **初始化**:使用两个哈希表 `need` 和 `window`,其中 `need` 记录字符串 `t` 中每个字符需要的数量,`window` 记录当前窗口中每个字符的数量。同时,初始化窗口的左右指针 `left` 和 `right` 都指向字符串 `s` 的起始位置,以及两个变量 `matched` 来记录当前窗口中满足 `t` 条件的字符个数,和 `result` 来记录最小覆盖子串的起始位置和长度。 2. **移动右指针**:不断移动右指针 `right`,扩大窗口,并更新 `window` 和 `matched`。如果 `window` 中某个字符的数量满足了 `need` 的要求,则 `matched` 加一。 3. **尝试移动左指针**:当 `matched` 等于 `t` 的长度时,表示当前窗口已经包含了 `t` 的所有字符。此时尝试移动左指针 `left` 来缩小窗口,并更新 `result`(如果当前窗口大小小于 `result` 记录的大小)。在移动左指针的过程中,如果移除了 `t` 中需要的字符,则 `matched` 减一。 4. **重复步骤 2 和 3**,直到右指针到达字符串 `s` 的末尾。 5. **返回结果**:如果找到了满足条件的最小覆盖子串,则返回该子串;否则,返回空字符串。 ### PHP 代码示例 ```php function minWindow($s, $t) { $need = array_count_values(str_split($t)); $window = []; $left = $right = 0; $matched = 0; $result = ['start' => 0, 'len' => PHP_INT_MAX]; while ($right < strlen($s)) { $c = $s[$right]; if (isset($need[$c])) { $window[$c] = isset($window[$c]) ? $window[$c] + 1 : 1; if ($window[$c] == $need[$c]) { $matched++; } } $right++; // 尝试缩小窗口 while ($matched == count($need)) { if ($right - $left < $result['len']) { $result = ['start' => $left, 'len' => $right - $left]; } $d = $s[$left]; $left++; if (isset($need[$d])) { if ($window[$d] == $need[$d]) { $matched--; } $window[$d]--; } } } return $result['len'] == PHP_INT_MAX ? "" : substr($s, $result['start'], $result['len']); } ``` ### Python 代码示例 ```python def minWindow(s: str, t: str) -> str: need = collections.Counter(t) window = collections.defaultdict(int) left, right = 0, 0 matched = 0 result = float('inf'), None, None while right < len(s): c = s[right] if c in need: window[c] += 1 if window[c] == need[c]: matched += 1 right += 1 while matched == len(need): if right - left < result[0]: result = (right - left, left, right) d = s[left] left += 1 if d in need: if window[d] == need[d]: matched -= 1 window[d] -= 1 return "" if result[1] is None else s[result[1]:result[2]] ``` ### JavaScript 代码示例 ```javascript function minWindow(s, t) { const need = {}; for (const char of t) { need[char] = (need[char] || 0) + 1; } let window = {}; let left = 0, right = 0; let matched = 0; let result = [0, Infinity]; // [start, length] while (right < s.length) { const c = s[right]; if (need[c]) { window[c] = (window[c] || 0) + 1; if (window[c] === need[c]) { matched++; } } right++; while (matched === Object.keys(need).length) { if (right - left < result[1]) { result = [left, right - left]; } const d = s[left]; left++; if (need[d]) { if (window[d] === need[d]) { matched--; } window[d]--; } } } return result[1] === Infinity ? "" : s.substring(result[0], result[0] + result[1]); } ``` 希望这些示例能帮助你理解和解决“最小覆盖子串”的问题。
推荐面试题