zoukankan      html  css  js  c++  java
  • leetcode 76 最小覆盖子串 滑动窗口

    在串s中找出包含串t的所有字符的最小子串。

    之前做了一些前缀和的题。想到用前缀和做:

    统计第k个字符在第i个位置出现的个数:pre[i][k] .则可以用 前缀和遍历所有的子串组合。然后我们及时的减支

    public String minWindow(String s, String t) {
    char[] tCharArray = t.toCharArray();
            char[] sCharArray = s.toCharArray();
            int lenS = s.length();
            int lenT = t.length();
            HashMap<Character,Integer> indx = new HashMap<>();
            int[] count = new int[lenT];
            for(int i=0;i<lenT;i++){
                if(!indx.containsKey(tCharArray[i]))
                    indx.put(tCharArray[i],i);
                count[indx.get(tCharArray[i])]++;
    
            }
            int charIndx = 0;
            int[][]precount = new int[lenS+1][lenT];
            for(int i=0;i<lenS;i++){
                for(int j=0;j<lenT;j++){
                    charIndx = indx.get(tCharArray[j]);
                    if(sCharArray[i]==tCharArray[j])
                        precount[i+1][charIndx]=precount[i][charIndx]+1;
                    else
                        precount[i+1][charIndx]=precount[i][charIndx];
                }
    
            }
            for(int i=0;i<lenT;i++){
                charIndx=indx.get(tCharArray[i]);
                if(precount[lenS][charIndx]<count[charIndx]) return "";
            }
            int dis = Integer.MAX_VALUE;
            int minLenJ=-1;
            int minLenI=-1;
            for(int i=0;i<lenS;i++){
                for(int j=i+1;j<lenS+1;j++){
                    if(dis<(j-i)) break;
                    boolean containFlag = true;
                    for(int k=0;k<lenT;k++){
                        charIndx=indx.get(tCharArray[k]);
                        if(precount[j][charIndx]-precount[i][charIndx]<count[charIndx]){
                            containFlag=false;
                            break;
                        }
                    }
                    if(containFlag){
                        if(dis>(j-i)){
                            minLenI=i;
                            minLenJ=j;
                            dis=j-i;
                        }
    
                    }
                }
            }
            if(minLenI>-1){
                return s.substring(minLenI,minLenJ);
            }
            return "";
    
        }
    View Code

    但是有个问题,如果s很大,t很大。就会超出内存限制。。。

    然后我用哈希表存储字符出现的个数,妄图遍历所有组合。不出意外的超时。

     public static boolean containsAll(HashMap<Character,Integer> src,HashMap<Character,Integer> subSrc){
            for(char c : src.keySet()){
                if(subSrc.getOrDefault(c,0)<src.get(c) ) return  false;
            }
            return true;
        }
        public static int[] shrink(char[] s,String t,int start,int end,HashMap<Character,Integer> src,HashMap<Character,Integer> subSrc){
            while (start<end){
                if(t.indexOf(s[start])!=-1){
                    subSrc.put(s[start],subSrc.get(s[start])-1);
                    if(containsAll(src,subSrc)){
                        start++;
                        continue;
                    }else{
                        break;
                    }
                }else{
                    start++;
                }
            }
            return new int[]{start,end+1};
        }
        public static String minWindow(String s, String t) {
            char[] tCharArray = t.toCharArray();
            char[] sCharArray = s.toCharArray();
            int lenS = s.length();
            int lenT = t.length();
            if(lenT>lenS) return "";
            int dis = Integer.MAX_VALUE;
            int ans[] = new int[2];
            HashMap<Character,Integer> count = new HashMap<>();
            for(int i=0;i<lenT;i++){
                count.put(tCharArray[i],count.getOrDefault(tCharArray[i],0)+1);
            }
            HashMap<Character,Integer> subCount = new HashMap<>();
            for(int i=0;i<lenS;i++){
                if(lenS-i<lenT) break;
                if(t.indexOf(sCharArray[i])!=-1){
                    subCount.clear();
                    for(int j=i;j<lenS;j++){
                        if(t.indexOf(sCharArray[j])!=-1){
                            subCount.put(sCharArray[j],subCount.getOrDefault(sCharArray[j],0)+1);
                            if(containsAll(count,subCount)){
                                int[] tmp = shrink(sCharArray,t,i,j,count,subCount);
                                if((tmp[1]-tmp[0])<dis){
                                    ans[0]=tmp[0];
                                    ans[1]=tmp[1];
                                    dis=tmp[1]-tmp[0];
                                }
    
                            }
                        }
                    }
                }
            }
            if(dis<Integer.MAX_VALUE) return s.substring(ans[0],ans[1]);
            return "";
    
        }
    View Code

    看了题解,改成滑动窗口。仍然用哈希表记录字符出现的个数:

      public static boolean containsAll(HashMap<Character,Integer> src,HashMap<Character,Integer> subSrc){
            for(char c : src.keySet()){
                if(subSrc.getOrDefault(c,0)<src.get(c) ) return  false;
            }
            return true;
        }
        public static int[] shrink(char[] s,String t,int start,int end,HashMap<Character,Integer> src,HashMap<Character,Integer> subSrc){
            while (start<end){
                if(t.indexOf(s[start])!=-1){
                    subSrc.put(s[start],subSrc.get(s[start])-1);
                    if(containsAll(src,subSrc)){
                        start++;
                        continue;
                    }else{
                        break;
                    }
                }else{
                    start++;
                }
            }
            return new int[]{start,end+1};
        }
        public static String minWindow(String s, String t) {
            char[] tCharArray = t.toCharArray();
            char[] sCharArray = s.toCharArray();
            int lenS = s.length();
            int lenT = t.length();
            if(lenT>lenS) return "";
            int dis = Integer.MAX_VALUE;
            int ans[] = new int[2];
            HashMap<Character,Integer> count = new HashMap<>();
            for(int i=0;i<lenT;i++){
                count.put(tCharArray[i],count.getOrDefault(tCharArray[i],0)+1);
            }
            HashMap<Character,Integer> subCount = new HashMap<>();
            int start = 0;
            int end = start;
            while (end<lenS){
                while(end<lenS){
                    if(t.indexOf(sCharArray[end])!=-1){
                        subCount.put(sCharArray[end],subCount.getOrDefault(sCharArray[end],0)+1);
                        if(containsAll(count,subCount)) break;
                    }
                    end++;
                }
                while (start<=end&&end<lenS){
                    if(t.indexOf(sCharArray[start])!=-1){
                        subCount.put(sCharArray[start],subCount.get(sCharArray[start])-1);
                        if(!containsAll(count,subCount)){
                            if(end-start<dis){
                                dis=end-start;
                                ans[0]=start;
                                ans[1]=end+1;
                            }
                            start++;
                            end++;
                            break;
                        }
                    }
                    start++;
                }
            }
            if(dis<Integer.MAX_VALUE) return s.substring(ans[0],ans[1]);
            return "";
    View Code

    一点都不优雅。继续看题解。发现优雅的解法。

    • t中的字符,走不出 ascii码,所以我们可以用一个 128长度的数组记录所有t中的字符,及它出现的次数。
    • 用一个整数记录已经匹配到字符数。

    这样就可以优雅的滑动窗口了:

     public static String minWindow2(String s, String t) {
            int lenS = s.length();
            int lenT = t.length();
            if(lenT==0||lenS<lenT)return "";
            char[] sC = s.toCharArray();
            char[] tC = t.toCharArray();
            int[] record = new int[128];
            for(int i=0;i<lenT;i++){
                record[tC[i]]++;
    
            }
            int left=0,right=0;
            int match = 0;
            int[] ans = new int[2];
            int dis = Integer.MAX_VALUE;
            while (right<lenS){
                record[sC[right]]--;
                if(record[sC[right]]>=0) match++;
                right++;
                while (match==lenT){
                    if(dis>right-left){
                        dis=right-left;
                        ans[0]=left;
                        ans[1]=right;
                    }
                    record[sC[left]]++;
                    if(record[sC[left]]>0) match--;
                    left++;
                }
            }
            return s.substring(ans[0],ans[1]);
    
        }
    View Code

    重点看一下滑动窗口这里:

         int left=0,right=0;
         int match = 0;
            int[] ans = new int[2];
            int dis = Integer.MAX_VALUE;
            while (right<lenS){
                record[sC[right]]--;
                if(record[sC[right]]>=0) match++;
                right++;
                while (match==lenT){
                    if(dis>right-left){
                        dis=right-left;
                        ans[0]=left;
                        ans[1]=right;
                    }
                    record[sC[left]]++;
                    if(record[sC[left]]>0) match--;
                    left++;
                }
            }

    record记录了t串中有多少个字符没有在窗口中。初始为t串中的所有字符。

    当窗口滑动时。我们把right指向的字符从record中减掉一个。这个时候,如果record上的字符个数>=0,说明匹配到一个t中的字符。match++。

    当match==lenT的时候,说明从letf到right的窗口中包含了t中所有字符串。此时我们收缩窗口。

    所以 recode中对应left的字符要加一个,如果字符个数>0,则说明拿走了一个t串中的字符,所以match要减一个。

    重复这个过程。直到right移动到s串外面。

  • 相关阅读:
    关于MYSQL 和INNODB的逻辑关系图。最好的理解是一点点动手做,观察,记录,思考。
    MYSQL的DOUBLE WRITE双写
    MYSQL 中binlog 参数的记录
    How to install pip
    gdb
    vim的基本使用
    012_fieldset.html
    010_header.html
    011_label.html
    008_img.html
  • 原文地址:https://www.cnblogs.com/superxuezhazha/p/12971471.html
Copyright © 2011-2022 走看看