【LeetCode】No.76. Minimum Window Substring -- Java Version

53 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情.

题目链接:leetcode.com/problems/mi…

1. 题目介绍(Minimum Window Substring)

Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every character in t (including duplicates) is included in the window. If there is no such substring, return the empty string "".

【Translate】: 给定两个长度分别为m和n的字符串s和t,返回s的最小窗口子字符串,使t中的每个字符(包括重复字符)都包含在窗口中。如果没有这样的子字符串,则返回空字符串""。

The testcases will be generated such that the answer is unique.

【Translate】: 生成的测试用例的答案是唯一的。

【测试用例】:

testcase

【条件约束】:

Constraints

【跟踪】:

follow up

2.题解

2.1 左右边界逼近找最小

原题解来自于tlj77的Share my neat java solutionmind1 子串的寻找过程如下图所示: mind2

class Solution {
    public String minWindow(String S, String T) {
        // 判空,返回空串
        if(S==null||S.isEmpty()||T==null||T.isEmpty()) return "";
        
        int i=0, j=0;
        
        // 定义满足ASCII表的两个数组,作为地图来记录字符串中各字符的数量
        int[] Tmap=new int[256];
        int[] Smap=new int[256];
        
        // 遍历T字符串,并记录字符串中含有的各字符数量
        for(int k=0; k< T.length(); k++){
            Tmap[T.charAt(k)]++;
        }
        
        // 通过found来匹配最小字符串,最好的情况就是found == t.length
        int found=0;
        int length=Integer.MAX_VALUE;
        String res="";
        
        // 遍历S字符串
        while(j<S.length()){
            // 如果found < t.length,那么说明此时肯定不满足条件,继续寻找
            if(found<T.length()){
                // Tmap中的当前S字符>0,说明T字符串中含有该字符,此时给Smap的该字符位置+1
                if(Tmap[S.charAt(j)]>0){
                    Smap[S.charAt(j)]++;
                    // 如果Smap的当前字符位置数 <= Tmap的数,说明此时的字符串仍满足题目的要求,找到了一个有效字符,让found++
                    if(Smap[S.charAt(j)]<=Tmap[S.charAt(j)]){
                        found++;
                    }
                }
                j++;
            }
            // 如果此时found == T.length,说明我们已经在S字符串中找到了满足T字符串的一个子串,接下来就是确定子串的范围,以及判断该子串是否为最小子串
            while(found==T.length()){
                
                // 确定子串
                if(j-i<length){
                    length=j-i; res=S.substring(i,j);
                }
                
                // 如果在Tmap中存在当前S字符的记录,那么删除Smap中的记录,如果删除后Smap记录数<Tmap,那么让found也--
                // 这个操作是为了向后查找,一直找到最小
                if(Tmap[S.charAt(i)]>0){
                    Smap[S.charAt(i)]--;
                    if(Smap[S.charAt(i)]<Tmap[S.charAt(i)]){
                        found--;
                    }
                }
                i++;
            }
        }
        return res;
    }
}

act1

下方题解来自于DyXrLxSTAOadoD在Share my neat java solution下的评论,比tlj77的更简洁也更强,但思想上是类似的。

Using count. 题意是找到str中最短的substring,它里面与t的所有字母对应的数量更多。 比如t里面有3个A,那么substring里面至少有3个A。 第一步,数一下t里面每个字母出现了多少次。 第二步,move end point,找到str中满足条件的字符串。就是刚好减掉了n个,n是t的长度。 第三步,move start point,去夹逼最小的substring,意思就是move start到不能往右移为止,多移一位substring就不满足条件。 第四步,比较长度。 第五步,把start右移一位,让substring不满足条件。 回到第二步。 mind3

class Solution {
  public String minWindow(String str, String t) {
  	  // 记录t中每个字母出现了多少次
      int[] map = new int[256];
      for(char c: t.toCharArray()){
        map[c - 'A']++;
      }
      
      int minLen = Integer.MAX_VALUE, minStart = 0;
  
      int n = t.length();
      char[] sc = str.toCharArray();
      // 定义子串的开始和结束边界
      int s = 0, e = 0;
      while(e < sc.length){
        int ie = sc[e] - 'A';
        map[ie]--;
        if(map[ie] >= 0){
          n--; 
        }
        
        if(n == 0){
          int is = sc[s] - 'A';
          while(map[is] < 0){
            map[is]++;
            s++;
            is = sc[s] - 'A';  
          }
          
          int len = e - s + 1;
          if(len < minLen){
            minLen = len;
            minStart = s;
          }
          
          map[is]++;
          s++;
          n++;
        }
        e++;
      }
  
      return minLen == Integer.MAX_VALUE ? "" : str.substring(minStart, minStart + minLen);
    }
}

act2