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
    }
}

全部评论

相关推荐

点赞 评论 收藏
分享
牛客339922477号:都不用reverse,直接-1。一行。啥送分题
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务