题目
思路
-
初始化变量:
start和minLen用于记录最小覆盖子串的起始位置和长度。初始长度设置为s.length() + 1,确保在找到更短的子串时会被更新。left和right是滑动窗口的左右边界指针。um是一个哈希表,用于记录字符串t中每个字符的出现频次。tlen用于记录t中还有几个字符没有被当前窗口包含。
-
构建哈希表:
- 遍历字符串
t,将每个字符及其出现频次存入哈希表um。
- 遍历字符串
-
移动右边界:
- 使用
while循环移动right指针,遍历字符串s。 - 每当遇到一个字符
s[right],将其在哈希表中的频次减 1。如果该字符是t中的字符(即减 1 后仍大于或等于 0),则将tlen减 1。
- 使用
-
检查并移动左边界:
-
当
tlen为 0 时,表示当前窗口包含了t中的所有字符。 -
进入
while循环,尝试缩小窗口大小:- 如果当前窗口长度
right - left小于minLen,更新minLen和start。 - 将左边界字符
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 "";
}
};