1. 处理特殊情况
目的:处理空字符串或无效输入,直接返回空结果。
2. 初始化字符计数器
目的:统计目标字符串 t
的字符需求,并为滑动窗口建立字符统计。
实现步骤:
- 使用哈希表(或数组)记录
t
中每个字符的出现次数。 - 定义窗口哈希表,动态记录当前窗口内字符的出现次数。
- 初始化有效计数
valid
,用于判断窗口是否覆盖t
的所有字符。
3. 滑动窗口主体框架
目的:用双指针维护窗口,右指针扩展窗口,左指针收缩窗口。
实现步骤:
- 定义左右指针
left=0, right=0
,初始化最小窗口长度和起始位置。 - 右指针遍历字符串,更新窗口统计。
- 当窗口满足覆盖条件时,收缩左指针寻找最小窗口。
4. 右指针扩展逻辑
目的:更新窗口字符计数,并检查是否满足 t
的需求。
关键点:
- 仅处理
t
中需要的字符。 - 当窗口内某字符数量达到
t
的需求时,valid
自增。
5. 判断窗口覆盖条件
目的:当 valid
等于 need
的字符种类数时,说明窗口已覆盖 t
。
6. 左指针收缩逻辑
目的:收缩窗口左边界,尝试找到更小的覆盖子串。
关键点:
- 左移时需检查移出的字符是否影响
valid
。 - 更新窗口统计时需反向操作(与右指针扩展相反)。
class Solution {
public:
string minWindow(string s, string t) {
if (s.empty() || t.empty() || s.length() < t.length()) return "";
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int valid = 0;
int left = 0, right = 0;
int start = 0, min_len = INT_MAX;
while (right < s.size()) {
// 右指针扩展
char c = s[right++];
if (need.count(c)) {
window[c]++;
if (window[c] == need[c]) {
valid++;
}
}
while (valid == need.size()){
if (right - left < min_len) {
min_len = right - left;
start = left;
}
char d = s[left++];
if (need.count(d)) {
if (window[d] == need[d]) {
valid--;
}
window[d]--;
}
}
}
return min_len == INT_MAX ? "" : s.substr(start, min_len);
}
};
一、时间复杂度分析
-
主循环结构
代码通过左右指针left
和right
实现滑动窗口,两个指针各遍历字符串s
一次(长度记为n
),因此时间复杂度为 O(n)。 -
哈希表操作
need
哈希表的初始化需要遍历字符串t
(长度记为m
),时间复杂度 O(m)。- 每次窗口滑动时的哈希表查询和更新操作均为 O(1)(假设哈希函数均匀分布)。
-
总体时间复杂度
综合主循环和哈希表操作,总时间复杂度为 O(n + m),满足线性复杂度要求