最小覆盖子串
题目描述
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:
如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
解法
class Solution {
public String minWindow(String s, String t) {
//空值判断
int sLen = s.length();
int tLen = t.length();
if (sLen == 0 || tLen == 0 || sLen < tLen) {
return "";
}
char[] tArr = t.toCharArray();
char[] sArr = s.toCharArray();
//初始化一个128数组
int[] tLetter= new int[128];
for (char c : tArr) {
tLetter[c]++;
}
//出现的字符的个数
int reduce = tLen;
//区间长度
int len = sLen+1;
int start = 0;
int left = 0;
int right = 0;
while (right < sLen) {
if (tLetter[sArr[right]] > 0) {
reduce--;
}
tLetter[sArr[right]]--;
right++;
//个数达到
while (reduce == 0) {
if ((right - left) < len) {
len = right - left;
start = left;
}
//左边的指针开始压缩
tLetter[sArr[left]]++;
if (tLetter[sArr[left]] > 0) {
reduce++;
}
left++;
}
}
if (len == sLen+1) {
return "";
}
return s.substring(start, start + len);
}
}
分析思路
首先承认由于这题属于困难等级,我的功力不够,也是参考别人的写法进行了逐步分解,理解,再写出。
1.根据题目的实例分析
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC
要求在S字符串找到最小包含T的字符的最小子串。
S字符串中可以找到 “ADOBEC”,“EBANC”,“BANC”,其中符合条件的只有结果 “BANC”
假设我们有left和right,两个指针标表示字串的下标分别为[0,5],[8,12],[9,12],==> s[left, right] 其中覆盖字符T。
1定义 left, right 分别覆盖区间的左右边界
int left = 0; int right = 0;
2.然后right指针开始右移动,直到覆盖了字符T,这里问题便出现了,如何知道s[left, right]中已经覆盖了T?
覆盖条件为含有全部的T字符,已知T的个数,我们假设一个变量int reduce = tLen;
当找到一个符合条件的字符,我们便减去一个,直到变量reduc为0,表示s[left, right]中已经覆盖了T
3。查找到一个已经覆盖T的子串,我们怎么知道当前的s[left, right]是否是最小的长度的字串?
我们初始化一个变量用于表示最短的字符的长度 int len = sLen(S字符的长度)+1;
这里将初始值设置为问slen(S字符的长度)+1,避免类似出现S=“A”,T=“B”, 如果 S 中不存这样的T子串,我们方便做一个判断
3.1 初始化 `int len = sLen+1;
3.2 对符合的字串,我们进行长度len的赋值
if ((right - left) < len) {
len = right - left;
}
4.知道字串的长度后,我们最终是要最S字符进行切割,这里就必须有两个变量beginIndex,endIndex
String substring(int beginIndex, int endIndex)
4.1 初始化一个变量 int start = 0;
便是我们要切割的字符起点
4.2 对符合字串的,我们进行起点start 的的赋值
if ((right - left) < len) {
len = right - left;
start = left;
}
5.我们已经知道第一个字串的s[left, right],right指针再右移动,已经没有意义,因为再往右边扩展再得到一个T字符,不满足题目的最小字串。既然 right不能移动,那我们试试动动left?
5.1现在要移动 left 指针,假如我们遇到在s[left]符合T字符的,这个时候我们应该做什么?
假设AXX,我们当前指针[left]=A,我们抛弃掉A,继续往XX滑动,之前我们设置了一个全局变量reduce表示,s[left, right]中符合T字符的个数,抛弃掉一个T字符,我们需要将reduce++;标识我们s[left++, right]中,reduce++只是增加,我right需要寻找T字符的个数,但是并不能知道需要寻找的字符是什么?
如:AXXBC,去掉A后,reduce++,right指针需要需要一个新的字符,维持字串reduce平衡,剩下的字符为XXBCB,right右移动,虽然维持了reduce,但是不符合我们的要求包含全部的T
一,维护一个map用于记录s[left, right]字串中的每个T字符的个数变化
二,维护一个长度为128(大小写字母)的int数组
这里我们使用了第二种方式
int[] tLetter= new int[128];
.......
tLetter[sArr[left]]++; 个数加1
if (tLetter[sArr[left]] > 0) {
reduce++;
}
left++;
6.这里我们基本上的代码基本上完成,还需要做一些
6.1 初始化数组,记录一开始每个T字符的个数
int[] tLetter= new int[128];
for (char c : tArr) {
tLetter[c]++;
}
6.2 遇到T字符减少格式
if (tLetter[sArr[right]] > 0) {
reduce--;
}
tLetter[sArr[right]]--;
于是整个代码进行dubug测试,完成提交