最小覆盖子串-java
题目描述 :
-
给你一个字符串
s
、一个字符串t
。返回s
中涵盖t
所有字符的最小子串。如果s
中不存在涵盖t
所有字符的子串,则返回空字符串""
。注意:
- 对于
t
中重复字符,我们寻找的子字符串中该字符数量必须不少于t
中该字符数量。 - 如果
s
中存在这样的子串,我们**保证它是唯一的答案。**
- 对于
**解题思想: **
- 滑动窗口算法是一种用于解决子字符串问题的经典方法。**其核心思想是通过维护一个窗口,根据特定条件移动窗口的左右边界来寻找满足要求的子串。**对于这个问题,我们要在字符串 s 中找到一个最小的子串,使得该子串包含字符串 t 中的所有字符。
- 该算法的时间复杂度为O(m+n),其中m是字符串s的长度,n是字符串t的长度。这是因为在移动左右边界的过程中,每个字符最多被访问两次,而不会存在重复遍历的情况。
解题思路如下 :
- 初始化两个频率数组**winFreq和tFreq**,用于记录窗口内字符的频率和字符串t中字符的频率。
- 遍历字符串t,更新**tFreq**数组,记录字符串t中每个字符出现的次数。
- 初始化窗口的左右边界**left和right,以及记录窗口起始位置begin和最小长度minLen。初始时,将left和right都置为0**,begin置为0,minLen置为s.length()+1。
- 开始移动右边界**right,扩大窗口,直到窗口包含了字符串t中的所有字符。每次移动右边界时,更新winFreq数组,并检查是否满足包含字符串t中所有字符的条件。如果满足条件,则开始移动左边界left**,缩小窗口,直到窗口不再满足条件为止。
- 在移动左右边界的过程中,不断更新最小长度**minLen和起始位置begin**。
- 最终返回起始位置**begin和最小长度minLen**所对应的子串。
代码实现如下:
class Solution {
public String minWindow(String s, String t){
if(s.isEmpty() || t.isEmpty() || s.length() < t.length())
return "";
int[] winFreq = new int[128];
int[] tFreq = new int[128];
for(char c : t.toCharArray())
tFreq[c]++;
int minLen = s.length() + 1;
int begin = 0;
int left = 0;
int right = 0;
int distance = 0;
while(right < s.length()){
if(tFreq[s.charAt(right)] == 0){
right++;
continue;
}
if(winFreq[s.charAt(right)] < tFreq[s.charAt(right)])
distance++;
winFreq[s.charAt(right)]++;
right++;
while(distance == t.length()){
if(tFreq[s.charAt(left)] == 0){
left++;
continue;
}
if(right - left < minLen){
minLen = right - left;
begin = left;
}
if(winFreq[s.charAt(left)] == tFreq[s.charAt(left)])
distance--;
winFreq[s.charAt(left)]--;
left++;
}
}
if(minLen == s.length() + 1)
return "";
return s.substring(begin, begin + minLen);
}
}
(begin, begin + minLen);
}
}
[外链图片转存中...(img-UrZ1A3OY-1711819077144)]