zoukankan      html  css  js  c++  java
  • 76.最小覆盖子串

    image-20200523170536726

    滑动窗口+哈希

    思路

    • 通过两个HashMap分别统计t中各字符的数量 滑动窗口中 含t中字符的数量

    • 滑动窗口不满足要求时,右指针向右移动 窗口不断扩张

    • 滑动窗口满足要求时,左指针向右移动 窗口收缩

    • 在窗口滑动过程中,同时记录并更新符合要求且宽度最小的 窗口信息

    • 通过 substring(ansL,ansR)返回

    代码

    /*
     * 134ms  7%
     */
    
    //ori 统计字符串t 中 各个字符的数量
        Map<Character,Integer> ori=new HashMap<>();
        //cnt 滑动字符串中 各字符的计数器;
        Map<Character,Integer> cnt=new HashMap<>();
        public String minWindow(String s,String t){
            //字符串t 的长度
            int tLen=t.length();
            //遍历字符串t  同时通过ori 记录t中各个字符的数量
            for(int i=0;i<tLen;i++){
                char c=t.charAt(i);
                ori.put(c, ori.getOrDefault(c, 0)+1);
            }
    
            //l表示左指针  r表示右指针
            int l=0,r=-1;
            //len记录 滑动窗口的最小长度   ansL记录窗口的左端  ansR记录窗口的右端
            int len=Integer.MAX_VALUE,ansL=-1,ansR=-1;
            //sLen表示字符串s的长度
            int sLen=s.length();
            while (r<sLen){
                //移动右指针
                r++;
                //如果r指向的字符在 t中,滑动字符串计数器cnt 记录该字符 并更新其数量
                if(r<sLen&&ori.containsKey(s.charAt(r))){
                    cnt.put(s.charAt(r), cnt.getOrDefault(s.charAt(r), 0)+1);
                }
                //先判断滑动字符串是否满足 t的要求,若满足,不断更新左指针(收缩窗口)
                while(check()&&l<=r){
                    //若新窗口值比旧窗口还小 更新ansL ansR
                    if(r-l+1<len){
                        len=r-l+1;
                        ansL=l;
                        ansR=l+len;
                    }
                    //收缩左指针  并更新cnt里的数量
                    if(ori.containsKey(s.charAt(l))){
                        cnt.put(s.charAt(l), cnt.getOrDefault(s.charAt(l), 0)-1);
                    }
                    l++;
                }
            }
            //直接调用substring方法截取字符串
            return ansL==-1?"":s.substring(ansL, ansR);
        }
    
        public boolean check(){
            Iterator iter=ori.entrySet().iterator();
            while(iter.hasNext()){
                Map.Entry entry=(Map.Entry)iter.next();
                Character key=(Character) entry.getKey();
                Integer val=(Integer) entry.getValue();
                if(cnt.getOrDefault(key, 0)<val){
                    return false;
                }
            }
            return true;
        }
    
    

    优化

    • 仅用一个字符数组记录要匹配字符及其频数

    代码

    /**
     * 5ms  90%
     */
    public String minWindow3(String s,String t){
            if (s == null || t == null || s.length() == 0 || t.length() == 0) return "";
            // 定义一个数字,用来记录字符串 t 中出现字符的频率,也就是窗口内需要匹配的字符和相应的频率
            int[] map = new int[128];
            for (char c : t.toCharArray()) {
                map[c]++;
            }
            int left = 0, right = 0;
            int match = 0;  // 匹配字符的个数
            int minLen = s.length() + 1;   // 最大的子串的长度
            // 子串的起始位置 子串结束的位置(如果不存在这样的子串的话,start,end 都是 0,s.substring 截取就是 “”
            int start = 0, end = 0;
            while (right < s.length()){
                char charRight = s.charAt(right); // 右边界的那个字符
                map[charRight]--;   // 可以理解为需要匹配的字符 charRight 减少了一个
                // 如果字符 charRight 在 t 中存在,那么经过这一次操作,只要个数大于等于 0,说明匹配了一个
                // 若字符 charRight 不在 t 中,那么 map[charRight] < 0, 不进行任何操作
                if (map[charRight] >= 0) match++;
                right++;  // 右边界右移,这样下面就变成了 [),方便计算窗口大小
    
                // 只要窗口内匹配的字符达到了要求,右边界固定,左边界收缩
                while (match == t.length()){
                    int size = right - left;
                    if (size < minLen){
                        minLen = size;
                        start = left;
                        end = right;
                    }
                    char charLeft = s.charAt(left);  // 左边的那个字符
                    map[charLeft]++;  // 左边的字符要移出窗口
                    // 不在 t 中出现的字符,移出窗口,最终能够达到的最大值 map[charLeft] = 0
                    // 如果恰好移出了需要匹配的一个字符,那么这里 map[charLeft] > 0, 也就是还要匹配字符 charLeft,此时 match--
                    if (map[charLeft] > 0) match--;
                    left++;  // 左边界收缩
                }
            }
            return s.substring(start, end);
    
        }
    

    参考链接:

    官方题解

    Kelly:Java 一个数组记录频数的滑动窗口

  • 相关阅读:
    仿IOS中下拉刷新的“雨滴”效果
    BZOJ 4216 Pig 分块乱搞
    mybatis学习笔记(10)-一对一查询
    关于人性,我是这么看的——“唯进化”论!
    IDEA引MAVEN项目jar包依赖导入问题解决
    IntelliJ IDEA 缓存和索引介绍和清理方法
    springboot整合mybatis使用阿里(阿里连接池)和xml方式
    Intellij 如何在新窗口中打开项目
    intellij idea 在什么地方打开终端Terminal
    Spring Boot 集成MyBatis
  • 原文地址:https://www.cnblogs.com/yh-simon/p/12943760.html
Copyright © 2011-2022 走看看