1.Leetcode 76.最小覆盖子串
思路:
用i,j表示滑动窗口的左边界和右边界,通过改变i,j来扩展和收缩滑动窗口,可以想象成一个窗口在字符串上游走,当这个窗口包含的元素满足条件,即包含字符串T的所有元素,记录下这个滑动窗口的长度j-i+1,这些长度中的最小值就是要求的结果。
我们可以用滑动窗口的思想解决这个问题。在滑动窗口类型的问题中都会有两个指针,一个用于「延伸」现有窗口的 rr 指针,和一个用于「收缩」窗口的 ll 指针。在任意时刻,只有一个指针运动,而另一个保持静止。我们在 ss 上滑动窗口,通过移动 rr 指针不断扩张窗口。当窗口包含 tt 全部所需的字符后,如果能收缩,我们就收缩窗口直到得到最小窗口。
如何判断当前的窗口包含所有 tt 所需的字符呢?我们可以用一个哈希表表示 tt 中所有的字符以及它们的个数,用一个哈希表动态维护窗口中所有的字符以及它们的个数,如果这个动态表中包含 tt 的哈希表中的所有字符,并且对应的个数都不小于 tt 的哈希表中各个字符的个数,那么当前的窗口是「可行」的。
class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char,int> needs,window; //needs中保存t中所有的字符,window是滑动窗口
for(char i:t) needs[i]++; //needs[i]如果不存在的话,会在map中创建这个键值,并且给其赋值为0
int left=0,right=0; //初始化 left = right = 0,把索引左闭右开区间 [left, right) 称为一个窗口。
//左闭右开区间是最方便处理的。因为这样初始化 left = right = 0 时区间 [0, 0) 中没有元素,但只要让 right 向右移动(扩大)一位,区间 [0, 1) 就包含一个元素 0 了。
int valid=0; //valid 变量表示窗口中满足 need 条件的字符的种类,如果 valid 和 need.size() 的大小相同,则说明窗口已满足条件,已经完全覆盖了串 T。
int start=0,len=INT_MAX;
while(right<s.size()){
char c=s[right]; //c是待移入窗口的字符
right++;
if(needs.count(c)!=0){
window[c]++;
if(window[c]==needs[c]){
valid++;
}
}
while(valid==needs.size()){
// 在这里更新最小覆盖子串
if((right-left)<len){
start=left;
len=right-left;
}
// d 是将移出窗口的字符
char d = s[left];
// 缩小窗口
left++;
// 进行窗口内数据的一系列更新
if (needs.count(d)!=0){
if (window[d] == needs[d]){
valid--;
}
window[d]--;
}
}
}
return len==INT_MAX?"":s.substr(start, len);//s.substr(pos, n)返回一个string,包含s中从pos开始的n个字符的拷贝。
}
};
2.Leetcode 567.字符串的排列
本题移动 left
缩小窗口的时机是窗口大小大于 t.size()
时,因为是排列,所以长度应该是一样的。
当发现 valid == need.size()
时,就说明窗口中就是一个合法的排列,立即返回 true
。
class Solution {
public:
bool checkInclusion(string s1, string s2) {
unordered_map<char,int> needs,window;
for(char i:s1) needs[i]++;
int left=0,right=0;
int valid=0;
while(right<s2.size()){
char c=s2[right];
right++;
if(needs.count(c)!=0){ //如果s2中的当前字符s2[right]在s1中,则把s2[right]加入到window中
window[c]++;
if(window[c]==needs[c]){ //如果window中的某一字符数量与s1中的该种字符数量相同,则valid++,最终判断的是valid是否等于s1.size(),用的是字符的种类进行比较
valid++;
}
}
while (right - left >= s1.size()) {
// 在这里判断是否找到了合法的子串
if (valid == needs.size())
return true;
char d = s2[left];
left++;
// 进行窗口内数据的一系列更新
if (needs.count(d)!=0) {
if (window[d] == needs[d]){
valid--;
}
window[d]--;
}
}
}
// 未找到符合条件的子串
return false;
}
};
3.Leetcode 438.找到字符串中所有字母异位词
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> res;
unordered_map<char,int> needs,window;
for(char i:p) needs[i]++;
int left=0,right=0;
int valid=0;
while(right<s.size()){
char c=s[right];
right++;
if(needs.count(c)!=0){
window[c]++;
if(window[c]==needs[c]){
valid++;
}
}
while(right-left>=p.size()){
if(valid==needs.size()){
res.push_back(left);
}
char d=s[left];
left++;
if(needs.count(d)!=0){
if(window[d]==needs[d]){
valid--;
}
window[d]--;
}
}
}
return res;
}
};
4,Leetcode 3.无重复字符的最长字串
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char,int> window;
int left=0,right=0;
int res=0;
while(right<s.size()){
char c=s[right];
right++;
window[c]++;
while(window[c]>1){ // 判断左侧窗口是否要收缩
char d=s[left];
left++;
window[d]--;
}
res = max(res, right - left);
}
return res;
}
};