Leetcode 76. 最小覆盖子串(滑动窗口+文本距离)

题解

  • 题意:给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串,顺序可以不一样。如果S并没有包含T的子串,那么返回"",否则,样例的S只会包含唯一的一个最短覆盖子串。
    T中可能包含重复字符,比如

输入: S = “ADOBECODEBAANC”, T = “AABC”
输出: “BAANC”

  • 题解:这题思路其实比一些中等题目简单(当然我是看题解才做出来的),查找字符串字串的题型考的最多的就是双指针滑动窗口的思路,但是这题还有一个点在于使用汉明距离来表示两个两个字符串的包含情况,这里首先介绍一下滑动窗口问题
    • 滑动窗口:指的是使用两个左右两个指针(l,r)维护一个窗口,窗口中容纳(r-l)长度的字符串。右指针向右滑动代表扩大窗口,左指针向右滑动代表缩小窗口。
      • 什么时候右指针向右移动?当前子串无法包含所有T中的元素的时候,需要扩大子串长度,使得子串的字符出现情况会包含T中的字符
      • 什么时候左指针向右移动?当前字串已经包含所有T中的元素,但是可能中间包含了很多无意义的重复的元素,找到的子串不是最小长度。
      • 使用minlen记录每次找到的子串的最短长度,如果变小了,就更新结果
    • 文本距离:表示两个字符串的字符差异情况,与顺序无关。
      • 上面的思路比较直接,唯一不直接的就是,如何判断字符串包含情况?如果遍历一遍,那就是o(n^2)的复杂度,铁定超时,因此我们需要找到一个简单的一点的判断方法
      • 按照概念,只需要S的子串的所有字符出现次数都大于等于T中所有字符出现的次数即可,那么我们的眼光就可以放到统计子串中每个字符出现的频率了
      • 如何统计呢,其实就是哈希映射,使用一个(char, int)的哈希表,将字符映射到频次,就可以得到每个字符出现的次数。
      • 为了判断子串和目标字符串的文本距离,这里建立两个映射,一个用于映射子串的字符频次winmap,另一个则映射目标字符串的频次tmap,比较winmap,tmap的差距,来更新文本距离dis, dis如何更新呢?
        • winmap[c] < tmap[c]的时候,说明c这个字符的频次在子串中还不够多,此时让dis++,然后让winmap[c]++,当dis==tmap.length的时候,说明已经找到符合规则的子串,但不一定是最短的,因此要记录下长度。然后调整滑动窗口,更新子串长度。
    • 总思路:最后,结合滑动窗口和文本距离,一开始让右指针向右滑动,如果出现的字符是T中出现的,那么就使该字符出现的频次++winmap[c]++,反之,当左指针向右滑动的时候,如果出现了T中出现的字符,就让对应字符频次–winmap[c]--,当dis==tmap.length的时候,说明这一段子串满足条件,但是不一定是最短的,此时让左指针向右移动,把多余的重复元素筛出来,直到不满足条件。
    • 总的来说,指针要么不动,要么向右,无论是左指针还是右指针,这里都不会出现向左移动的情况。
  • 实现:由于向上转型char类型放到int类型数组中自动转换为ascii码,如果严谨一点应该使用Map
class Solution {
    public String minWindow(String s, String t) {
        int l =0, r = 0;
        int tlen = t.length();
        int n = s.length();
        int min_len = n+1;
        char[] chars = s.toCharArray();
        char[] chart = t.toCharArray();
        //Map<Character, Integer> mapwin = new HashMap<Character, Integer>();
        //Map<Character, Integer> mapt = new HashMap<Character, Integer>();
        int []mapwin = new int[128];
        int []mapt = new int[128];
        
        int dis = 0;
        for(char c:chart) mapt[c]++; 
        int resl = 0, resr = 0;

        while(r < n ){
            if(mapt[chars[r]] == 0){
                ++r;
                continue;
            }        
            if(mapwin[chars[r]] < mapt[chars[r]])    
                ++dis;     
            mapwin[chars[r]]++;    
            ++r;
            while(l < r && dis >= tlen){
                if(mapt[chars[l]] > 0){
                    
                    if(mapwin[chars[l]] == mapt[chars[l]])    
                        --dis;
                    mapwin[chars[l]]--;     
                }
                ++l; 
            } 
            
            if(l>0&&dis == tlen-1 && min_len > r-l){
                resl = l-1;
                resr = r;
                min_len = r-l;
            }
        }
        return resr > resl?s.substring(resl, resr):"";
    }
}

题目

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值