经典滑动窗口问题
滑动窗口算法的思路是这样:
1、我们在字符串 S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right] 称为一个「窗口」。
2、我们先不断地增加 right 指针扩大窗口 [left, right],直到窗口中的字符串符合要求(包含了 T 中的所有字符)。
3、此时,我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right],直到窗口中的字符串不再符合要求(不包含 T 中的所有字符了)。同时,每次增加 left,我们都要更新一轮结果。
4、重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头。
这个思路其实也不难,第 2 步相当于在寻找一个「可行解」,然后第 3 步在优化这个「可行解」,最终找到最优解。左右指针轮流前进,窗口大小增增减减,窗口不断向右滑动。
可以用两个哈希表当作计数器解决。用一个哈希表 needs 记录字符串 t 中包含的字符及出现次数,用另一个哈希表 window 记录当前「窗口」中包含的字符及出现的次数,如果 window 包含所有 needs 中的键,且这些键对应的值都大于等于 needs 中的值,那么就可以知道当前「窗口」符合要求了,可以开始移动 left 指针了。
C++
一、最小字符串覆盖
题目描述
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。
示例:
输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”
说明:
如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
class Solution {
public:
string minWindow(string s, string t) {
int minLen = INT_MAX;
int start = 0;
unordered_map<char,int>windows;//定义哈希表,把它做成一个字典;
unordered_map<char,int>needs;//定义哈希表,把它做成一个字典;
for(char c:t){
needs[c]++;//初始化一个t字典;
}
int len=s.size();
int left=0,right=0;
int match=0;
//这时,要做的是移动right,将windows窗口内包含t中所有的字符;
while(right<len){
char c1=s[right];
if(needs.count(c1)){
windows[c1]++;
//windows中的c1键值等于needs中的c1键值时,匹配数加一;
if(windows[c1]==needs[c1]){
match++;
}
}
right++;
//这时,windows里面已经包含了所有的t中的字符,接下来要做的是移动
//left去优化windows窗口;
while(match==needs.size()){
//这里是判断是否达到最后的windows中字符长度;
//这是求最小子串的判断条件
if(right-left<minLen){
minLen=right-left;
start = left;
}
char c2 = s[left];
//如果c2这个字符在needs中,
//则将windows中c2的键值--;
if(needs.count(c2)){
windows[c2]--;
//当windows中的c2的键值小于needs(即t中的字符数)中键值,
//则匹配数(match)减一;
if(windows[c2]<needs[c2]){
match--;
}
}
left++;
}
}
return minLen==INT_MAX?"":s.substr(start,minLen);
}
};
二、
这题的关键是判断条件是由原来的求最小子串,改为了求子串,那么直接就是求子串与目标串的大小相等
题目描述:
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。
字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
说明:
字母异位词指字母相同,但排列不同的字符串。
不考虑答案输出的顺序。
示例 1:
输入:
s: “cbaebabacd” p: “abc”
输出:
[0, 6]
解释:
起始索引等于 0 的子串是 “cba”, 它是 “abc” 的字母异位词。
起始索引等于 6 的子串是 “bac”, 它是 “abc” 的字母异位词。
示例 2:
输入:
s: “abab” p: “ab”
输出:
[0, 1, 2]
解释:
起始索引等于 0 的子串是 “ab”, 它是 “ab” 的字母异位词。
起始索引等于 1 的子串是 “ba”, 它是 “ab” 的字母异位词。
起始索引等于 2 的子串是 “ab”, 它是 “ab” 的字母异位词。
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int>res;
int len = s.length();
int left=0,right=0;
unordered_map<char,int>windows;
unordered_map<char,int>needs;
int match=0;
for(char c:p){
needs[c]++;
}
while(right<len){
char c1=s[right];
if(needs.count(c1)){
windows[c1]++;
if(needs[c1]==windows[c1]){
match++;
}
}
right++;
while(match==needs.size()){
//这里的判断条件才是最关键的
if(right-left==p.length()){
res.push_back(left);
}
char c2 = s[left];
if(needs.count(c2)){
windows[c2]--;
if(windows[c2]<needs[c2]){
match--;
}
}
left++;
}
}
return res;
}
};