目录
一.最大连续1的个数 III
算法思想:
不断入窗口,如果扫描为0,则可看作翻转为1,计数器cnt++,然后判断计数器反转次数是否大于K,大于则出窗口直到出掉一个0使cnt--,然后重新记录长度len取最大值即可。
class Solution {
public:
int longestOnes(vector<int>& nums, int k){
int cnt=0,len=0;
int left=0,right=0;
while(right<nums.size())
{
//进窗口
if(nums[right]==0)
{
cnt++;
}
//判断
if(cnt>k)
{
//出窗口
while(left<right&&nums[left]!=0)
{
left++;
}
left++;
cnt--;
}
//重新记录
len=max(len,right-left+1);
right++;
}
return len;
}
};
二.将 x 减到 0 的最小操作数
算法思想:正难则反,这里可以先将数组的sum算出,然后sum-x算出target即滑动窗口的和,然后保证滑动窗口内和为target,求最大长度的滑动窗口len,最后返回num.size()-len即可。
PS:这里先将len设为-1,如果最后len是否-1要分类讨论。
class Solution {
public:
int minOperations(vector<int>& nums, int x){
//正难则反
int sum=0;
for(int i=0;i<nums.size();i++)
sum+=nums[i];
//滑动窗口的和值
sum-=x;
//求最长滑动窗口
int left=0,right=0;
int len=-1,win_sum=0;
while(right<nums.size())
{
//进窗口
win_sum+=nums[right];
//判断
while(left<=right&&win_sum>sum)
{
//出窗口
win_sum-=nums[left];
left++;
}
//如果符合就记录结果
if(win_sum==sum)
len=max(len,right-left+1);
right++;
}
if(len==-1)
return -1;
else
return nums.size()-len;
}
};
三.水果成篮
算法思想:可以用哈希表记录是否已经采摘过扫描指针指向的水果(入窗口),如果没有,则令type++,然后判断当type>2时,则将left++(出窗口),令哈希表中left指向的水果种类-1直到为0时,type--,保证type<=2,然后记录结果长度len。
PS:我们将结果记录放在判断之后是因为,我们认为扫描指针前的结果已经记录下来,所以只需从扫描指针开始再次更新结果即可。
class Solution {
public:
int totalFruit(vector<int>& fruits)
{
int hash[100100]={0};
int left=0,right=0;
int len=0,type=0;
while(right<fruits.size())
{
//进窗口
hash[fruits[right]]++;
//判断
if(hash[fruits[right]]==1)
{
//种类数+1
type++;
while(type>2)
{
//出窗口
hash[fruits[left]]--;
if(hash[fruits[left]]==0)
type--;
left++;
}
}
//记录结果
len=max(len,right-left+1);
right++;
}
return len;
}
};
四. 找到字符串中所有字母异位词
算法思想:依旧用哈希表,先记录p子串哈希表,然后滑动窗口遍历s串,当s的哈希数据开始大于p时,开始出窗口直到符合位置,当窗口长度与p长度相等时说明符合,记录当前索引。
class Solution {
public:
vector<int> findAnagrams(string s, string p)
{
vector<int> res;
//提前判断
if(s.size()<p.size())
return res;
int hash1[128]={0};
int hash2[128]={0};
//先用哈希表记录子串的数据,不在乎顺序
for(int i=0;i<p.size();i++)
hash1[p[i]]++;
int left=0,right=0;
while(right<s.size())
{
//进窗口
hash2[s[right]]++;
//判断,当与子串某一位置的哈希数据不符时则出窗口
while(left<=right&&hash2[s[right]]>hash1[s[right]])
{
//出窗口
hash2[s[left]]--;
left++;
}
//当长度相等时说明子串匹配
if(right-left+1==p.size())
{
res.push_back(left);
}
right++;
}
return res;
}
};
五.最小覆盖子串(. - 力扣(LeetCode))
算法思想:用哈希表记录t数据,用滑动窗口遍历s串,每次入窗口时检查一下是否窗口的哈希数据全部大于等于t,如果成立说明窗口已经覆盖t串,记录数据(索引)并开始出窗口直到不符为止跳出判断。最后找到最小长度的索引,拷贝一份返回即可。
bool check(int*hash1,int*hash2)
{
for(int i=0;i<128;i++)
{
if(hash2[i]<hash1[i])
return false;
}
return true;
}
class Solution {
public:
string minWindow(string s, string t)
{
string a("");
if(s.size()<t.size())
return a;
int hash1[128]={0};
int hash2[128]={0};
for(int i=0;i<t.size();i++)
{
hash1[t[i]]++;
}
int left=0,right=0;
int len=INT_MAX,mark=-1;
while(right<s.size())
{
//进窗口
hash2[s[right]]++;
//判断
while(check(hash1,hash2))
{
//更新结果
if(right-left+1<rlen)
{
len=right-left+1;
mark=left;
}
//出窗口
hash2[s[left]]--;
left++;
}
right++;
}
if(mark==-1)
return a;
else
return s.substr(mark,len);
}
};
六。串联所有单词的子串(. - 力扣(LeetCode))
算法思想:因为word中所有单词长度相等,可以将每个单词看作不同的abc字母,按照第四题的异位词解题方法来用哈希表解决问题(以单词长度len为单位移动,经过len次遍历)。
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words)
{
vector<int> ret;
unordered_map<string,int> hash1;//记录每个单词出现的频次(以单词为单位)
for(auto &s:words)
hash1[s]++;//不考虑单词内部具体顺序,只关注单词整体的哈希数据
//len为单词的长度也即要总的遍历的次数,m为单词的个数
int len=words[0].size(),m=words.size();
for(int i=0;i<len;i++)
{
//记录s串遍历过程中窗口内的哈希数据
unordered_map<string,int> hash2;
//记录窗口内已经匹配的单词个数
int count=0;
for(int left=i,right=i;right+len<=s.size();right+=len)
{
//入窗口+维护conut
string in=s.substr(right,len);
hash2[in]++;
//输入的单词匹配则增加count个数,说明已经匹配上
if(hash1.count(in)&&hash2[in]<=hash1[in]) count++;
//判断,当窗口长度大于words中所有单词长度串联总和时出窗口
while(right-left+1>len*m)
{
//出窗口+维护count
string out=s.substr(left,len);
//事先判断words数组里有无这个扫描单词,节省时间开销
if(hash1.count(out)&&hash2[out]<=hash1[out]) count--;
hash2[out]--;
left+=len;
}
//更新结果,当窗口有m个元素单词匹配时,说明完全匹配,记录索引
//这里保证了窗口长度不可能大于所有单词长度串联总和,所以不会有其他多余的无关单词
if(count==m) ret.push_back(left);
}
}
return ret;
}
};