BM90 题解 | #最小覆盖子串#
最小覆盖子串
https://www.nowcoder.com/practice/c466d480d20c4c7c9d322d12ca7955ac
官方题解:双指针 + 哈希数组
说在前面:
因为26个字母算上大小写一共就52位,并且每一位的下标都可以由字符对应的ASCII码来获得
(比如'a' = 97,hash['a'] -> hash[97])
所以这题不需要使用HashMap,使用数组作为哈希结构就行了。
思路:
这个解法的思路就是使用一个哈希数组来记录T中每个字符的出现次数
- 哈希数组在初始化的时候把T中每个字符对应的位置的值-1。
- 如果是AbA,那么'A'(ASCII为60)对应在哈希数组中就表现为:hash[60] = -2, 'b':hash[98] = -1
- 使用双指针进行遍历:
- fast指针向右移动一位,就把当前这一位在哈希数组中对应的次数+1
- 然后判断当前哈希数组中是否有负数的值
- 如果哈希数组中存在负值,说明T中还有元素没有被包含进来的。
- 如果哈希数组中所有值都为正,说明当前窗口内的元素已经可以包含T中的所有字符了。
- 记录当前最优解
- 移动slow指针。为什么是移动slow指针?因为是从左往右遍历的,所以缩小窗口不能把fast指针往左缩小。所以要把左指针往右移动,看看是不是移动之后还能满足T中的所有字符,求一个最小长度
注释很详细
import java.util.*;
public class Solution {
// 本题使用的是一个哈希数组
// 检查是否有小于0的元素
boolean check(int[] hash) {
for (int i = 0; i < hash.length; ++i) {
if (hash[i] < 0)
return false;
}
return true;
}
public String minWindow (String S, String T) {
// 记录结果子串的字符个数
int count = S.length() + 1;
// a-z为97-122。A-Z为65-90。所以这里数组长度才设了一个128,其实真正使用的只有26*2个
// 这个哈希数组中存放了a-z A-Z出现的次数
// 初始化的时候T中的元素全都-=。其他元素都不管
// 后续在遍历的时候,每遍历到一个元素,就在这个元素对应的位置上+1
// 这样,T之外的元素不受影响,T里面的元素满足了个数就会 >=0
// 我们只要查看哈希数组中是否有0以下的数,如果有,就说明窗口没有完全包含T,如果没有,就说明已经完全包含T了
int[] hash = new int[128];
for (int i = 0; i < T.length(); ++i) {
hash[T.charAt(i)] -= 1;
}
// 双指针
int slow = 0, fast = 0;
// 记录左右区间
int left = -1, right = -1;
for (; fast < S.length(); ++fast) {
char c = S.charAt(fast);
// 遍历到字符就把哈希数组中对应的元素+1
hash[c]++;
// 如果滑动窗口中没有小于0的元素说明都被覆盖了。
// 此时窗口中的元素已经能包含T中的所有元素了
// 这个时候fast还在当前轮次,slow开始向右移动,以寻求更短的,符合条件的子串
while (check(hash)) {
// 取最短的
if (count > fast - slow + 1) {
count = fast - slow + 1; // 记录当前最优解
left = slow; // 记录当前窗口
right = fast;
}
c = S.charAt(slow);
// 缩小窗口
hash[c]--; // 因为是向右收缩窗口,所以把最左边的元素从窗口中剔除
slow++;
}
}
// 找不到的情况
// 上面left的初始化就是-1,并且此时没有任何返回,说明left还没有动过
// 因为一旦找到了,left就移到slow的位置,所以当left == -1时一定是没找到
if (left == -1) {
return "";
}
return S.substring(left, right + 1); // 左闭右开所以要多+1
}
}

