开启掘金成长之旅!这是我参与「掘金日新计划 · 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】: 生成的测试用例的答案是唯一的。
【测试用例】:
【条件约束】:
【跟踪】:
2.题解
2.1 左右边界逼近找最小
原题解来自于tlj77的Share my neat java solution。
子串的寻找过程如下图所示:
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;
}
}
下方题解来自于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不满足条件。 回到第二步。
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);
}
}