1. 题⽬链接:76.最⼩覆盖⼦串
2. 题⽬描述:
3. 解法(滑动窗⼝+哈希表):
算法思路:
◦ 研究对象是连续的区间,因此可以尝试使⽤滑动窗⼝的思想来解决。
◦ 如何判断当前窗⼝内的所有字符是符合要求的呢?
我们可以使⽤两个哈希表,其中⼀个将⽬标串的信息统计起来,另⼀个哈希表动态的维护窗⼝ 内字符串的信息。
当动态哈希表中包含⽬标串中所有的字符,并且对应的个数都不⼩于⽬标串的哈希表中各个字 符的个数,那么当前的窗⼝就是⼀种可⾏的⽅案。
算法流程:
a. 定义两个全局的哈希表: 1 号哈希表hash1 ⽤来记录⼦串的信息, 2 号哈希表hash2 ⽤来记录⽬标串t 的信息;
b. 实现⼀个接⼝函数,判断当前窗⼝是否满⾜要求:
i. 遍历两个哈希表中对应位置的元素:
• 如果t 中某个字符的数量⼤于窗⼝中字符的数量,也就是2 号哈希表某个位置⼤于 1 号哈希表。说明不匹配,返回false ;
• 如果全都匹配,返回true 。
主函数中:
a. 先将t 的信息放⼊2 号哈希表中;
b. 初始化⼀些变量:左右指针: left = 0,right = 0 ;⽬标⼦串的⻓度: len = INT_MAX ;⽬标⼦串的起始位置: retleft ;(通过⽬标⼦串的起始位置和⻓度,我们就 能找到结果)
c. 当right ⼩于字符串s 的⻓度时,⼀直下列循环:
i. 将当前遍历到的元素扔进1 号哈希表中;
ii. 检测当前窗⼝是否满⾜条件:
• 如果满⾜条件:
◦ 判断当前窗⼝是否变⼩。如果变⼩:更新⻓度len ,以及字符串的起始位置 retleft ;
◦ 判断完毕后,将左侧元素滑出窗⼝,顺便更新1 号哈希表;
◦ 重复上⾯两个过程,直到窗⼝不满⾜条件;
iii. right++ ,遍历下⼀个元素;
d. 判断len 的⻓度是否等于INT_MAX :
i. 如果相等,说明没有匹配,返回空串;
ii. 如果不想等,说明匹配,返回s 中从retleft 位置往后len ⻓度的字符串。
C++算法代码:
class Solution
{
public:
string minWindow(string s, string t)
{
int kind = 0; //记录字符串s中的元素种类
unordered_map<char, int>hash1; //子字符串中元素的出现次数
for (int i = 0; i < t.size(); i++)
{
if (hash1[t[i]]++ == 0)
{
kind++;
}
}
unordered_map<char, int>hash2; //查找字符串中元素的出现次数
//滑动窗口
int count = 0; //记录满足字符串s的元素个数
int begin; //答案的起始位置
int key_minsize = INT_MAX; //答案的最短长度
bool value = false; //判断是否有答案
for (int left = 0, right = 0; right < s.size(); right++)
{
//进窗口
hash2[s[right]]++;
//判断
//如果出现次数要比子串只多不少,满足则计数器加1
if (hash1[s[right]]&&hash2[s[right]] == hash1[s[right]])
{
count++;
}
//出窗口
while (count == kind)
{
if (key_minsize > right - left + 1)
{
value = true;
begin = left;
key_minsize = right - left + 1;
}
//当退窗口的字母在子串中存在,且满足条件则计数器减1
if (hash1[s[left]] && hash2[s[left]] == hash1[s[left]])
{
count--;
}
hash2[s[left]]--;
left++;
}
}
if (value)
{
return s.substr(begin,key_minsize);
}
else
{
return "";
}
}
};
Java算法代码:
class Solution {
public String minWindow(String ss, String tt) {
char[] s = ss.toCharArray();
char[] t = tt.toCharArray();
int[] hash1 = new int[128]; // 统计字符串 t 中每⼀个字符的频次
int kinds = 0; // 统计有效字符有多少种
for (char ch : t)
if (hash1[ch]++ == 0) kinds++;
int[] hash2 = new int[128]; // 统计窗⼝内每个字符的频次
int minlen = Integer.MAX_VALUE, begin = -1;
for (int left = 0, right = 0, count = 0; right < s.length; right++)
{
char in = s[right];
if (++hash2[in] == hash1[in]) count++; // 进窗⼝ + 维护 count
while (count == kinds) // 判断条件
{
if (right - left + 1 < minlen) // 更新结果
{
minlen = right - left + 1;
begin = left;
}
char out = s[left++];
if (hash2[out]-- == hash1[out]) count--; // 出窗⼝ + 维护 count
}
}
if (begin == -1) return new String();
else return ss.substring(begin, begin + minlen);
}
}