最小覆盖子串
编号:0017
题目来源:leetcode
试题描述
给定一个字符串S
,一个字符串T
,请在字符串S
中找出包含T
所有字符的最小子串
- 如果
S
中不存在这样的子串,则返回空字符串""
- 如果
S
中存在这样的子串,测试用例保证它是唯一的答案
解答算法
算法思路
这道题用到的还是滑动窗口算法。简单的来说,就是维护两个指针begin
和end
,先移动end
,当能够满足begin
和end
中子串满足条件的时候,停止移动end
,转而移动begin
,如果移动之后产生的子串不满足条件了,那么移动之前的子串就是可能的最短字串,否则继续移动begin
。当移动之后不满足,继续重复上述过程。
实现代码
class Solution {
public:
unordered_map <char, int> ori, cnt; //map存储了对应的子串和目标串的各个字母的个数
//cnt存储的是窗口子串,ori存储的是目标串
bool check() { //判断当前子串是否满足条件
for (const auto &p: ori) { //对于ori中每个元素都进行遍历
if (cnt[p.first] < p.second) {
return false;
}
}
return true;
}
string minWindow(string s, string t) {
for (const auto &c: t) { //先初始化ori
++ori[c];
}
int l = 0, r = -1;
int len = INT_MAX, ansL = -1, ansR = -1; //初始化左右标记
while (r < int(s.size())) { //遍历S中元素
if (ori.find(s[++r]) != ori.end()) { //只记录在ori中存在的字母,找到了就数目++。注意这里也对r进行了自增操作
++cnt[s[r]];
}
while (check() && l <= r) { //如果满足条件,就移动窗口左侧
if (r - l + 1 < len) { //更新左侧
len = r - l + 1;
ansL = l;
}
if (ori.find(s[l]) != ori.end()) {//更新了其中的内容
--cnt[s[l]];
}
++l;更新l
}
}
return ansL == -1 ? string() : s.substr(ansL, len);
}
};
复杂度分析
- 时间复杂度: O ( n ) O(n) O(n)。整个过程中,左右指针进行移动,最坏情况就是左右指针都从头移动到尾,也就进行两次遍历,时间复杂度为 O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1),整个过程中,申请了常数个变量,以及两个map存储字母个数,字母最多有26个,因此是常数的空间复杂度