Leetcode 76. 最小覆盖子串(困难) 567. 字符串的排列(中等) 438. 找到字符串中所有字母异位词(中等) 3. 无重复字符的最长子串(中等) 滑动窗口算法
labuladong讲解
76. 最小覆盖子串(困难)
题目:
思路:
在s中找出包含t所有字母的最小子串。
使用滑动窗口算法,在need哈希表中保存t所有字母的次数。
先扩展right,如果s[right]在need中存在,就增加window[c]次数,如果window[c]==need[c],则valid++
当valid>=need.size时,判断子串大小是否需要存储,然后移动left,如果s[left]在need中存在,如果window[d]==need[d],valid--
class Solution { public: string minWindow(string s, string t) { unordered_map<char,int> need,window; int valid=0; // 记录最小覆盖子串的起始索引及长度 int len=INT_MAX; int start=0; int left=0,right=0; for(int i=0;i<t.size();++i){ need[t[i]]++; } while(right<s.size()){ // c 是将移入窗口的字符 char c=s[right]; // 右移窗口 right++; // 进行窗口内数据的一系列更新 if(need.count(c)>0){ window[c]++; if(window[c]==need[c]){ valid++; } } // 判断左侧窗口是否要收缩 while(valid==need.size()){ // 在这里更新最小覆盖子串 if(right-left<len){ len=right-left; start=left; } // d 是将移出窗口的字符 char d=s[left]; // 左移窗口 left++; // 进行窗口内数据的一系列更新 if(need.count(d)>0){ if(window[d]==need[d]){ valid--; } window[d]--; } } } // 返回最小覆盖子串 string ret; if(len<INT_MAX){ ret=s.substr(start,len); } return ret; } };
567. 字符串的排列(中等)
题目:
思路:
相当给你一个 S
和一个 T
,请问你 S
中是否存在一个子串,包含 T
中所有字符且不包含其他字符
对于这道题的解法代码,基本上和最小覆盖子串一模一样,只需要改变两个地方:
1、本题移动 left
缩小窗口的时机是窗口大小大于 t.size()
时,应为排列嘛,显然长度应该是一样的。
2、当发现 valid == need.size()
时,就说明窗口中就是一个合法的排列,所以立即返回 true
class Solution { public: // 判断 s 中是否存在 t 的排列 bool checkInclusion(string s1, string s2) { unordered_map<char,int> need,window; int left=0,right=0; int valid=0; for(int i=0;i<s1.size();++i){ need[s1[i]]++; } while(right<s2.size()){ char c=s2[right]; right++; // 进行窗口内数据的一系列更新 if(need.count(c)>0){ window[c]++; if(window[c]==need[c]) valid++; } // 判断左侧窗口是否要收缩 while(right-left>=s1.size()){ // 在这里判断是否找到了合法的子串 if(valid==need.size()){ return true; } // 进行窗口内数据的一系列更新 char d=s2[left]; left++; if(need.count(d)>0){ if(window[d]==need[d]) valid--; window[d]--; } } } // 未找到符合条件的子串 return false; } };
438. 找到字符串中所有字母异位词(中等)
题目:
思路:
相当于,输入一个串 S
,一个串 T
,找到 S
中所有 T
的排列,返回它们的起始索引。
class Solution { public: vector<int> findAnagrams(string s, string p) { unordered_map<char,int> need,window; int left=0,right=0; // 记录结果 vector<int> ret; int valid=0; for(int i=0;i<p.size();++i){ need[p[i]]++; } while(right<s.size()){ char c=s[right]; right++; // 进行窗口内数据的一系列更新 if(need.count(c)>0){ window[c]++; if(window[c]==need[c]) valid++; } // 判断左侧窗口是否要收缩 while(right-left>=p.size()){ // 当窗口符合条件时,把起始索引加入 res if(valid==need.size()){ ret.push_back(left); } char d=s[left]; left++; // 进行窗口内数据的一系列更新 if(need.count(d)>0){ if(window[d]==need[d]) valid--; window[d]--; } } } return ret; } };
3. 无重复字符的最长子串(中等)
题目:
思路:
连 need
和 valid
都不需要,而且更新窗口内数据也只需要简单的更新计数器 window
即可。
当 window[c]
值大于 1 时,说明窗口中存在重复字符,不符合条件,就该移动 left
缩小窗口
需要注意的是,要在收缩窗口完成后更新 res
,因为窗口收缩的 while 条件是存在重复元素,换句话说收缩完成后一定保证窗口中没有重复嘛。
class Solution { public: int lengthOfLongestSubstring(string s) { unordered_map<char,int> window; int left=0,right=0; int ret=0; while(right<s.size()){ char c=s[right]; right++; // 进行窗口内数据的一系列更新 window[c]++; // 判断左侧窗口是否要收缩 while(window[c]>1){ char d=s[left]; left++; // 进行窗口内数据的一系列更新 window[d]--; } // 在这里更新答案 ret=max(ret,right-left); } return ret; } };
联系方式:emhhbmdfbGlhbmcxOTkxQDEyNi5jb20=