Leetcode 76. 最小覆盖子串

58 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情

1.题目

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:

对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。 如果 s 中存在这样的子串,我们保证它是唯一的答案。  

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"

2.思路

本题需要我们找到字符串s中能够涵盖t所有字符的最小子串,我们可以考虑通过滑动窗口的方式来解决这个问题, 因为首先我们需要能够覆盖t的所有字符,并不要求顺序和连续性,所以我们不采用动态规划的方法来解决,因为并不能将问题分解成小问题来解决。

我们可以通过一个滑动窗口的形式先向右扩张直到找到t中要求的所有字符,然后当确认已经全部找到后开始缩小滑动窗口的大小并记录当前大小,直到滑动窗口中的元素不再满足覆盖t的字符串,此时又开始滑动窗口的右指针直到再次满足覆盖t字符串的要求或者右指针到达末尾。

首先我们通过一个hash表来记录当前窗口内仍需要满足的字符串要求,并用变量记录hash表长度,以便后续使用该变量来判断当前窗口状态,每次新增与删除都修改该变量的数值,来控制窗口的左右滑动。然后每次滑动都需要判断修改了记录表中的哪一个字段,并判断是否还缺乏字段。比较记录大小与当前窗口大小的值并更新维护,当最后右指针滑动到末尾时返回当前记录大小。

3.代码

var minWindow = function(s, t) {
    let newSet = new Map()
    let res = ''
    let r = 0;
    let l = 0
    
    for(let i of t){
        newSet.set(i,newSet.has(i)?newSet.get(i)+1:1)
    }
    let size = newSet.size
    
    while(r<s.length){
        if(newSet.has(s[r])){
            
            newSet.set(s[r],newSet.get(s[r])-1)
            if(newSet.get(s[r])==0){
                size-=1
            }
        }
        while(size===0){
            res = res.length===0?s.substring(l,r+1): s.substring(l,r+1).length>res.length?res:s.substring(l,r+1)
            if(newSet.has(s[l])){
                newSet.set(s[l],newSet.get(s[l])+1)
                if(newSet.get(s[l])===1){
                    
                    size+=1
                }
                
            }
        l+=1
        }
                    
        
        r+=1
    }
    return res
};