最小覆盖子串

1、题目描述

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
在这里插入图片描述

2、解答思路

本题的主要思路是枚举 s 子串的右指针 right,如果子串覆盖字符串 t,就不断移动左指针 left 使得窗口达到最小值,直到不覆盖为止。在移动过程中更新最短子串的左右端点。我们可以想到解题思路,但是在O(1)的时间复杂度下判断子串是否覆盖字符串 t 是个难点。这里使用less变量,即子串中有less种字母的个数小于t中字母的个数。比如子串是 “ab”, t 是 “aab”,那么子串中还有一种字母的个数小于 t 中字母的个数,即 ‘a’ 的个数。

  • 若less>0,说明子串仍未完全覆盖字符串 t,右指针继续往右移动,即窗口继续扩大。
  • 若less=0,说明子串完全覆盖字符串 t,t 中所有字母的个数都小于等于子串中对应字母的个数。

那么如何判断 less 的增减呢,这里使用 int cnt[]数组,以字母的ascii码为索引,首先存储字符串 t 的各个字母个数,右指针 right 往后遍历字符串 s 的时候 cnt[ch]的数量减一,如果 cnt[ch]==0,那么说明当前窗口中的 ch 数量等于字符串 t 中 ch 数量,种类数 less 减一;在左指针往右遍历的时候,如果 cnt[ch]==0,说明往右遍历之后窗口中 cnt[ch] 出现的次数会小于 t 中 cnt[ch] 出现的次数,那么less++。

class Solution {
    public String minWindow(String S, String t) {
        // 将字符串转换为数组,方便用[索引]取元素
        char[] s = S.toCharArray();
        int m = s.length;
        // 左右指针
        int right = 0, left = 0;
        // 最终结果的左右指针(由于之后涉及到ansRight-ansLeft的比较,因此这里分别取m和-1)
        int ansRight = m, ansLeft = -1;
        // 由于最大的ascii码值为127,因此这里的数组长度为128
        int[] cnt = new int[128];
        // 子串中有less种字母的个数小于t中字母的个数
        int less = 0;
        
        // 当前子串为空,遍历t中字符并得到字母个数(cnt[])以及不同字母的个数(less)
        for(char ch: t.toCharArray()){
            // cnt[ch]==0说明新的字母出现
            if(cnt[ch]==0)
                less++;
            cnt[ch]++;
        }

        // 右指针依次向右遍历
        for(right = 0; right < m; right++){
            char ch = s[right];
            // 无需判断t中是否包含ch
            // 若t中不包含ch,则值为负数,不影响后续结果
            cnt[ch]--;
            // cnt[ch] == 0说明当前窗口中的ch数量==t中ch数量,种类数减一
            if(cnt[ch] == 0){
                less--;
            }
            while(less==0){
                // 更新最小窗口的左右指针
                if(right-left < ansRight-ansLeft){
                    ansRight = right;
                    ansLeft = left;
                }
                
                // 窗口的左边界向右移动,得到尽可能小的符合条件的子串
                ch = s[left++];
                cnt[ch]++;
                // 左指针右移之后cnt[ch]>0,说明窗口中x出现的次数小于t中x出现的次数,那么less++
                if(cnt[ch] > 0){
                    less++;
                }
            }
        }
        return ansLeft < 0 ? "" : S.substring(ansLeft, ansRight + 1);
    }
}

  • 注意字符串s的字母大小写任意,因此不宜使用s[ch-‘a’]的方式,直接将字母的ascii码作为索引会更方便。
  • 时间复杂度:O(m+n+∣Σ∣),其中 m 为 s 的长度,n 为 t 的长度,∣Σ∣=128。
  • 空间复杂度:O(∣Σ∣),其中∣Σ∣=128。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值