[面试精选] 0076. 最小覆盖子串
文章目录
- 1. 题目链接
- 2. 题目描述
- 3. 题目示例
- 4. 解题思路
- 5. 题解代码
- 6. 复杂度分析
1. 题目链接
76. 最小覆盖子串 - 力扣(LeetCode)
2. 题目描述
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
注意:
- 对于
t
中重复字符,我们寻找的子字符串中该字符数量必须不少于t
中该字符数量。 - 如果
s
中存在这样的子串,我们保证它是唯一的答案。
3. 题目示例
示例 1 :
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
示例 2 :
输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。
4. 解题思路
- 问题理解:
- 给定一个字符串
S
和一个字符串t
,需要在S
中找到包含t
所有字符的最短子串。 - 子串必须包含
t
的所有字符,且字符的出现次数不少于t
中的出现次数。
- 给定一个字符串
- 关键思路:
- 滑动窗口:使用双指针(
left
和right
)维护一个窗口,通过移动指针动态调整窗口大小。 - 哈希表统计:使用数组
cntS
和cntT
分别统计窗口内字符和t
中字符的出现次数。 - 窗口有效性检查:通过
isCovered
方法检查当前窗口是否满足条件。
- 滑动窗口:使用双指针(
- 算法流程:
- 初始化
cntT
数组,统计t
中字符的出现次数。 - 使用双指针
left
和right
遍历S
:right
右移,扩展窗口,更新cntS
。- 当窗口满足条件时,尝试收缩
left
指针,寻找更小的窗口。 - 记录最小窗口的左右边界。
- 返回最小窗口对应的子串。
- 初始化
5. 题解代码
class Solution {public String minWindow(String S, String t) {char[] s = S.toCharArray(); // 将字符串S转为字符数组int m = s.length; // 字符串S的长度int ansLeft = -1; // 记录最小窗口的左边界,初始为-1表示未找到int ansRight = m; // 记录最小窗口的右边界,初始为m(字符串长度)// cntS数组记录当前窗口内各字符的出现次数int[] cntS = new int[128]; // ASCII码范围0-127// cntT数组记录字符串t中各字符的出现次数int[] cntT = new int[128];// 初始化cntT数组for (char c : t.toCharArray()) {cntT[c]++;}int left = 0; // 滑动窗口的左指针for (int right = 0; right < m; right++) { // 滑动窗口的右指针cntS[s[right]]++; // 将右指针指向的字符加入窗口// 检查当前窗口是否包含t的所有字符while (isCovered(cntS, cntT)) { // 如果当前窗口比之前记录的最小窗口更小if (right - left < ansRight - ansLeft) { ansLeft = left; // 更新最小窗口的左边界ansRight = right; // 更新最小窗口的右边界}cntS[s[left]]--; // 将左指针指向的字符移出窗口left++; // 左指针右移}}// 如果找到了符合条件的窗口,返回对应的子串;否则返回空字符串return ansLeft < 0 ? "" : S.substring(ansLeft, ansRight + 1);}// 检查cntS是否包含cntT的所有字符(即cntS中各字符的数量都不小于cntT)private boolean isCovered(int[] cntS, int[] cntT) {// 检查大写字母for (int i = 'A'; i <= 'Z'; i++) {if (cntS[i] < cntT[i]) {return false;}}// 检查小写字母for (int i = 'a'; i <= 'z'; i++) {if (cntS[i] < cntT[i]) {return false;}}return true;}
}
6. 复杂度分析
- 时间复杂度:
- 遍历
S
的时间复杂度为 O(m),其中m
是S
的长度。 isCovered
方法的时间复杂度为 O(1)(因为字符集大小固定为52个字母)。- 总体时间复杂度为 O(m)。
- 遍历
- 空间复杂度:
cntS
和cntT
数组的大小为 O(128) = O(1)。- 其他变量占用常数空间。
- 总体空间复杂度为 O(1)。