题目
给定两个字符串 s 和 t 。返回 s 中包含 t 的所有字符的最短子字符串。如果 s 中不存在符合条件的子字符串,则返回空字符串 “” 。
如果 s 中存在多个符合条件的子字符串,返回任意一个。
注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
解释:最短子字符串 “BANC” 包含了字符串 t的所有字符 ‘A’、‘B’、‘C’
示例 2:
输入:s = “a”, t = “a”
输出:“a”
示例 3:
输入:s = “a”, t = “aa”
输出:“”
解释:t 中两个字符 ‘a’ 均应包含在 s的子串中,因此没有符合条件的子字符串,返回空字符串。
提示:
1 <= s.length, t.length <= 105
s 和 t 由英文字母组成
进阶:你能设计一个在 o(n) 时间内解决此问题的算法吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/M1oyTv
一、解题思路及代码
这道困难题可以说是此中等题的进阶版了,看不懂代码的朋友可以先去看看这题:
剑指 Offer II 014. 字符串中的变位词(滑动窗口与双指针好例题)
此题的解法很多,有的用数组记录出现的字符串,有的用hash表,有的用滑动窗口。(不要脸的说句,我的执行用时 2ms 也可以算是比较快的了哈哈)
思路:
- 定义两个数组cnt1,cnt2,分别将字符串 t ,s 中的字符转化成整数类型作为数组的下标,字符出现的次数作为值
- 定义differ,记录cnt1数组中不为0的值数量。
- 初始化 双指针left 与 right 为0,双指针只能往右移动,找到以下标 right 结尾,且含有 t 所有字符的最小区间[left,right].
关键判断条件:当cnt2[n]>=cnt1[n],也就是cnt2中 n 所代表的字符出现次数,比cnt1中 n 所代表的字符出现次数多或相等,也就是已找到 n 所代表的所有字符。
代码如下:
class Solution {
public String minWindow(String s, String t) {
int n = s.length();
int m = t.length();
//输出的字符串,初始为空
String res = "";
if(n<m) return res;
//cnt1为t的数组,cnt2为s的数组
int[] cnt1 = new int[60];
int[] cnt2 = new int[60];
for(int i=0;i<m;i++){
cnt1[t.charAt(i)-'A']++;
}
//diff标记不同,当为0时代表已找到
int diff = 0;
for(int i:cnt1){
if(i!=0) diff++;
}
//left,right双指针
int left = 0;
int min=Integer.MAX_VALUE;
for(int right=0;right<n;right++){
//cnt2[x]++后如果等于cnt1[x],代表字符 * 已全部找到
int x = s.charAt(right)-'A';
cnt2[x]++;
if(cnt1[x]==cnt2[x]) diff--;
while(diff==0){
//对比赋值res
int l = right-left+1;
if(l<min){
min=l;
res=s.substring(left,right+1);
}
//cnt1[y]!=0表示 t 含有此字符
//只要cnt2[y]的值不小于cnt1[y],那么结果不影响
//如果小于则diff++;
int y = s.charAt(left)-'A';
cnt2[y]--;
if(cnt1[y]!=0&&cnt2[y]<cnt1[y]){
diff++;
}
left++;
}
}
return res;
}
}
力扣官方解析也是今天刚出,不过他的没优化,执行用时 93ms。(我的快多啦哈哈哈,小得意)。
题目来自力扣
侵删