分析
-
本题的考点:滑动窗口。
-
这一题和LeetCode 0030 串联所有单词的子串十分类似。区别有两点:(1)这一题窗口中是字符,不是字符串;(2)这一题窗口中可以有多余字符,而LC30要求窗口内单词种类和次数与给定的完全相同。
-
本题可以使用双指针算法中的滑窗解决,滑窗为
[j, i]
,这里的j
是满足题意最大的j
,假设窗口内包含t
中所有字符,则i
增加的时候,j
不会减小,至少保持不变,否则如果j
减小的话可以用反正法证明不符合j
的定义。
- 剩下的问题是如何判断滑窗
s[j, i]
中完全包含t
中的所有字符?类似于LC30,我们可以开两个哈希表hs, ht
,分别记录滑窗中各个字符出现次数、t
中各字符出现次数,然后使用一个变量cnt
统计有效字符的数量,所谓有效字符数量是指:如果当前考察的字符s[i]
在滑窗中出现次数小于t
中出现次数,就是有效的。
代码
- C++
class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char, int> hs, ht; // hs: 滑窗s[j, i]中各字符数量; ht: t中各字符数量
for (auto c : t) ht[c]++;
string res;
int cnt = 0; // 有效字符数量
for (int i = 0, j = 0; i < s.size(); i++) {
hs[s[i]]++;
if (hs[s[i]] <= ht[s[i]]) cnt++;
while (j < s.size() && hs[s[j]] > ht[s[j]]) hs[s[j++]]--; // 滑窗中存在多余的字符s[j]
if (cnt == t.size()) {
if (res.empty() || i - j + 1 < res.size())
res = s.substr(j, i - j + 1);
}
}
return res;
}
};
- Java
class Solution {
public String minWindow(String s, String t) {
char[] cs = s.toCharArray(), ct = t.toCharArray();
// hs: 滑窗s[j, i]中各字符数量; ht: t中各字符数量
int[] hs = new int[128], ht = new int[128];
for (char c : ct) ht[c]++;
String res = "";
int cnt = 0;
for (int i = 0, j = 0; i < cs.length; i++) {
hs[cs[i]]++;
if (hs[cs[i]] <= ht[cs[i]]) cnt++;
while (j < cs.length && hs[cs[j]] > ht[cs[j]]) hs[cs[j++]]--;
if (cnt == ct.length) {
if (res.equals("") || i - j + 1 < res.length())
res = s.substring(j, i + 1);
}
}
return res;
}
}
时空复杂度分析
-
时间复杂度:$O(n)$,
n
为字符串s的长度。 -
空间复杂度:$O(n)$。