滑动窗口算法 / 双指针算法
定义两个哈希表,一个 hs 用于存储窗口 [i, j] 中字符出现的次数,一个 ht 用于存储字符串 t 中每个字符出现的次数。
定义一个变量 cnt 用于记录窗口中包含字符串 t 的有效字符个数(多的不算)。
先将 t 中每个字符和出现次数存入哈希表,然后遍历字符串 s
滑动窗口的常规步骤:
-
将当前字符 s[i] 加入到窗口中,
hs[s[i]] ++
-
判断当前字符是否为有效字符
hs[s[i]] <= ht[s[i]]
,如果是则 cnt + 1 -
尝试更新指针 j,如果当前字符 s[j] 是多余字符(窗口内的 s[j] 字符出现次数已经大于字符串 t 中的这个字符)
hs[s[j]] > ht[s[j]]
,则将当前字符出现次数 - 1 同时指针后移 -
更新答案。如果答案为空或者比之前的长度更小,则更新
class Solution {
public:
string minWindow(string s, string t) {
// hs 是用于来记录窗口 [i, j] 中每个字符出现的次数,ht 是用来记录字符串 t 中每个字符出现的次数
unordered_map<char, int> hs, ht;
for (auto c : t) ht[c] ++ ;
string res;
// 表示窗口 [i, j] 中有效的字符个数(有效:如果加上当前字符 s[i],能让 cnt 加 1 就是有效字符,否则就是无效字符)
int cnt = 0;
for (int i = 0, j = 0; i < s.size(); i ++ ) {
// 把 s[i] 加到滑动窗口中
hs[s[i]] ++ ;
// 如果当前字符是有效字符,那么窗口内有效字符个数 + 1
if (hs[s[i]] <= ht[s[i]]) cnt ++ ;
// 尝试移动 j
while (hs[s[j]] > ht[s[j]]) hs[s[j ++ ]] -- ;
// 如果当前有效窗口个数等于 t 中的字符个数,更新答案
if (cnt == t.size()) {
if (res.empty() || i - j + 1 < res.size())
res = s.substr(j, i - j + 1);
}
}
return res;
}
};