1. 题目
给你一个字符串 S、一个字符串 T,请在字符串 S里面找出:包含 T所有字母的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:
如果 S 中不存这样的子串,则返回空字符串 ""。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
链接:https://leetcode-cn.com/problems/minimum-window-substring
2. 思路
(1) 对于这类求子串问题,首先想到的方法就是滑动窗口,即通过双指针先后遍历字符串,在遍历过程中对当前窗口进行判断,当满足条件时更新记录,直至遍历完成。

(2) 那么如何在搜索过程中确定当前窗口是否满足条件呢?众所周知,hash表的查找速度是最快的,我们尝试使用hash表将目标字符和需求出现次数做一个映射,例如T=“AAABBC”,那么建立映射
MAP:{A:3, B:2, C:1},在头指针前进过程中,若头指针所指向元素存在于MAP中,则使对应元素映射值-1,当所有映射值小于零的时候(在满足条件之前可能有的映射值会变为负数,为缩短条件判断时间,采用一个变量记录归零值的数量,当该变量等于MAP的长度时,条件被满足),认为当前窗口符合条件,开始窗口收缩(尾指针开始追赶),尾指针指向元素存在于MAP中时,使映射值+1,当任意映射值大于零时,就获得了一个最小窗口。继续移动头指针,开始下一轮的搜索。
3. 代码
class Solution {
public String minWindow(String s, String t) {
if (s.length() == 0 || t.length() == 0) {
return "";
}
// 建立映射计数
Map<Character, Integer> temp = new HashMap<>();
for (char c : t.toCharArray()) {
temp.put(c, temp.getOrDefault(c, 0) + 1);
}
// 字符串转换为数组
char[] ss = s.toCharArray();
// 建立双指针
int head = 0, tail = 0;
// 记录最小宽度 以及当时尾指针位置
int min = s.length();
int mt = -1;
// 开始搜索
for (int size = temp.size(); head < s.length(); head++) {
// 计数搜寻(头指针向后移动)
if (temp.keySet().contains(ss[head])) {
Integer num = temp.get(ss[head]);
temp.put(ss[head], num - 1);
if (num == 1)
size--;
}
// 缩小范围(当窗口满足条件,尾指针开始追赶;不满足条件时停止追赶)
if (size == 0) {
do {
if (temp.keySet().contains(ss[tail])) {
Integer num = temp.get(ss[tail]) + 1;
temp.put(ss[tail], num);
if (num == 1)
size++;
}
tail++;
} while (size == 0);
// 记录最小值
if (head - tail + 1 < min) {
mt = tail - 1;
min = head - mt;
}
}
}
return mt == -1 ? "" : s.substring(mt, mt + min + 1);
}
}
4. 复杂度
设 m 为 T 的长度,n 为 S 的长度
时间复杂度:O(m+n)
空间复杂度:O(n)

本文介绍了一种解决字符串子串问题的有效算法——滑动窗口法,并通过LeetCode上的一道经典题目“最小覆盖子串”来详细阐述其原理与实现。文章提供了完整的Java代码示例,展示了如何利用哈希表优化搜索过程,找到包含特定字符集的最短子串。
761

被折叠的 条评论
为什么被折叠?



