无重复字符的最长子串
思路:用set维护一个滑动窗口,保证其中没有重复元素,如果遇到重复元素就从头部开始弹出
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int l = 0, ans = 0;
unordered_set<char> st;
for(int i=0; i<s.size(); i++)
{
while(st.find(s[i]) != st.end())
{
st.erase(s[l]);
l++;
}
st.insert(s[i]);
ans = max(ans, i-l+1);
}
return ans;
}
};
找到字符串中所有字母异位词
思路:自定义一个数组用来检测,然后维护一个长度为p.size()的滑动窗口.
优化点:可以记录p中出现了哪些字母,如果出现了一个从未出现过的字母,那就可以跳过长的长度,重新开始
上面的是负优化.这题没在这上面卡人,但这个优化是可以学一下的.
未优化版本
class Solution {
public:
int cnt[27], mycnt[27];
bool check(int x)
{
for(int i=0; i<26; i++)
{
if(cnt[i] != mycnt[i]) return false;
}
return true;
}
vector<int> findAnagrams(string s, string p) {
vector<int> ans;
if(s.size() < p.size()) return ans;
for(int i=0; i<p.size(); i++)
cnt[p[i]-'a']++;
for(int i=0; i<p.size(); i++)
mycnt[s[i]-'a']++;
for(int i=0; i+p.size() <= s.size(); i++)
{
if(check(i)) ans.push_back(i);
//cout<<"cnt: ";
//for(int i=0; i<26; i++) cout<<cnt[i]<<" ";
//cout<<endl;
//cout<<"mycnt: ";
//for(int i=0; i<26; i++) cout<<cnt[i]<<" ";
//cout<<endl;
if(i+1+p.size() <= s.size())
{
mycnt[s[i]-'a']--;
mycnt[s[i+p.size()]-'a']++;
}
}
return ans;
}
};
优化版本
class Solution {
public:
int cnt[27], mycnt[27];
bool check(int x)
{
for(int i=0; i<26; i++)
{
if(cnt[i] != mycnt[i]) return false;
}
return true;
}
vector<int> findAnagrams(string s, string p) {
vector<int> ans;
set<char> st;
if(s.size() < p.size()) return ans;
for(int i=0; i<p.size(); i++)
{
if(st.find(p[i]) == st.end()) st.insert(p[i]);
}
for(int i=0; i<p.size(); i++)
cnt[p[i]-'a']++;
for(int i=0; i<p.size(); i++)
mycnt[s[i]-'a']++;
//remake记录是否需要重新录入
bool remake = false;
for(int i=0; i+p.size() <= s.size(); i++)
{
//未找到则标记 并跳过这个字母
if(st.find(s[i]) == st.end())
{
remake = true;
continue;
}
// 通过remake判断是否需要重新录入
if(remake)
{
for(int i=0; i<26; i++) mycnt[i] = 0;
for(int j = i; j<i+p.size(); j++)
mycnt[s[j]-'a']++;
}
if(check(i)) ans.push_back(i);
if(i+1+p.size() <= s.size())
{
mycnt[s[i]-'a']--;
mycnt[s[i+p.size()]-'a']++;
}
}
return ans;
}
};
滑动窗口最大值
思路:使用一个滑动窗口去维护最大值,前面的用滑动窗口长度去排除,后面的用数值关系去排除
class Solution {
public:
int a[100010];
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int l = 0, r = -1;
vector<int> ans;
for(int i=0; i<nums.size(); i++)
{
if(l<=r && i-a[l] >= k) l++;
while(l<=r && nums[a[r]] <= nums[i]) r--;
a[++r] = i;
if(i >= k-1)ans.push_back(nums[a[l]]);
}
return ans;
}
};
最小覆盖子串
思路:思想有点类似双指针,还是维护一个滑动数组,如果l下标的数量多了,l左移,r随着for循环移动,每一次都去检查check,如果成立那就更新答案
注:这里我是用数组模拟的,但其实可以用map来做
class Solution {
public:
int cnt[128], nowCnt[128];
int findcnt(char x)
{
if(x >= 'A' && x <= 'Z')
return cnt[x - 'A'];
if(x >= 'a' && x <= 'z')
return cnt[x - 'a' + 30];
return 1;
}
int findNowcnt(char x)
{
if(x >= 'A' && x <= 'Z')
return nowCnt[x - 'A'];
if(x >= 'a' && x <= 'z')
return nowCnt[x - 'a' + 30];
return 1;
}
void cntadd(char x, int d)
{
if(x >= 'A' && x <= 'Z')
cnt[x - 'A']+= d;
if(x >= 'a' && x <= 'z')
cnt[x - 'a' + 30]+= d;
}
void cntNowadd(char x, int d)
{
if(x >= 'A' && x <= 'Z')
nowCnt[x - 'A'] += d;
if(x >= 'a' && x <= 'z')
nowCnt[x - 'a' + 30] += d;
}
bool useable(char x)
{
if(x >= 'A' && x <= 'Z')
return findcnt(x) > 0;
if(x >= 'a' && x <= 'z')
return findcnt(x) > 0;
return true;
}
bool check()
{
for(int i=0; i<56; i++)
if(cnt[i] > nowCnt[i]) return false;
return true;
}
string minWindow(string s, string t) {
for(int i=0; i<t.size(); i++)
{
cntadd(t[i], 1);
}
int l = 0, r;
bool finded = false;
int minans = 3e5;
int ansl = -1, ansr = -1;
string ans = "";
for(r=0; r<s.size(); r++)
{
//此时r这个字母是否是有用的
if(!useable(s[r])) continue;
cntNowadd(s[r], 1);
while(l<r && ((findNowcnt(s[l]) > findcnt(s[l])) || findcnt(s[l])==0))
{
if(useable(s[l])) cntNowadd(s[l], -1);
l++;
}
if(check() && (r - l + 1) < minans)
{
finded = true;
minans = (r - l + 1);
ansl = l, ansr = r;
}
}
if(finded) ans = s.substr(ansl, ansr - ansl +1);
return ans;
}
};