我的LeetCode:https://leetcode-cn.com/u/ituring/
我的LeetCode刷题源码[GitHub]:https://github.com/izhoujie/Algorithmcii
LeetCode 76. 最小覆盖子串
题目
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:
- 如果 S 中不存这样的子串,则返回空字符串 ""。
- 如果 S 中存在这样的子串,我们保证它是唯一的答案。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-window-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
很自然能想到的是用滑动窗口来解决;
题目有个坑就是,对于s="aaa"和t="aa",答案是"aa"而不是"a",就是说,子串必须包含t的所有字母数量至少是t的长度;
思路1-滑动窗口
因为本题参数都是字母,故可以转为数组来处理;
步骤:
- 创建t对应的数组need并统计其字母数量为窗口参照,创建用来搜索s的窗口数组search,窗口双指针left和right从s的0位置开始,记录匹配到t中字母的计数器count和最小覆盖子串的下标记录数组min;
- right开始遍历s,统计到search,且若字母在need存在,则count++且最多统计need中的上限值;
- 当count等于t的长度时表明找到了一个覆盖子串,此时停止right,从left开始缩小窗口,若找到了更小的坐标则更新min;
算法复杂度: n1为s的长度,n2为t的长度
- 时间复杂度: $ {color{Magenta}{Omicronleft(n1+n2 ight)}} $
- 空间复杂度: $ {color{Magenta}{Omicronleft(1 ight)}} $
算法源码示例
package leetcode;
import java.util.HashMap;
/**
* @author ZhouJie
* @date 2020年3月6日 下午9:41:21
* @Description: 76. 最小覆盖子串
*
*/
public class LeetCode_0076 {
}
class Solution_0076 {
/**
* @author: ZhouJie
* @date: 2020年3月6日 下午11:46:51
* @param: @param s
* @param: @param t
* @param: @return
* @return: String
* @Description: 2-按照题意要求,将1的改造一下,hash哈希表存对应字符出现次数;
* cache改为map类型,思路不变,但是改为校验cache的map与has是否完全一致即可;
*
* 因为题目中的都是字母,所以不用HashMap改用128长度的数组足矣,
*
*/
public String minWindow_2(String s, String t) {
int tLen = 0, sLen = 0;
// 特例判断
if (t == null || (tLen = t.length()) == 0) {
return "";
}
// 特例判断
if (s == null || (sLen = s.length()) < tLen) {
return "";
}
// 需要找到的字母和数量,转为数组记录
int[] need = new int[128];
for (char c : t.toCharArray()) {
need[c]++;
}
// 滑动窗口中的字母和数量
int[] search = new int[128];
// 滑动窗口左右指针和统计匹配到t中字母的计数count
int left = 0, right = 0, count = 0;
int[] min = new int[] { 0, sLen };
while (right < sLen) {
char c = s.charAt(right);
// 记录s中当前字母数量,若是need中要找的,则count++且最多加need中的上限个
search[c]++;
if (need[c] > 0 && need[c] >= search[c]) {
count++;
}
// 若s中的字母在t中全找到了,则开始从左侧缩小窗口
while (count == tLen) {
c = s.charAt(left);
if (need[c] > 0 && need[c] >= search[c]) {
count--;
}
// 若找到了更小的窗口则更新
if (right - left < min[1] - min[0]) {
min[0] = left;
min[1] = right;
}
search[c]--;
left++;
}
right++;
}
// 需要验证min[1],未找到时返回"",否则截取s返回,记得截取右侧位置要+1
return min[1] == sLen ? "" : s.substring(min[0], min[1] + 1);
}
}