滑动窗口法#
什么情况下会想到滑动窗口法:
任何题目如果没有思路其实都可以想一下暴力解法。这道题暴力解法思路简单:
遍历任意i,j,使得i和j之间的子串长度,等于p串的长度。该子串称之为x。该步复杂度为O(n)。
判断x是否与p是异位词。是的话,则把i加入答案中。该步复杂度为O(n)。
暴力法的复杂度为O(n^2)。显然不高效。
可以发现第二步其实做了很多不必要的操作,例如[i, j]和[i+1, j+1]两个子串在暴力法第二步中,需要各遍历一次,完全没必要。其实[i+1, j+1]完全可以在[i, j]的基础上做判断,也就是去掉头部的字符(i位置),加上尾部的字符(j+1位置)。这样第一步的复杂度可以降到O(1)。整体复杂度降到O(n)。已经得到信息不重复使用就浪费了,没必要重新搜集近乎相同的信息。这就是滑动窗口法。
滑动窗口法的特点是,一连串元素的信息,可以用常数时间推断出,该串整体移位后,新串信息。
所有滑动窗口问题,如果能从暴力法优化的角度思考,都不难想到。
双指针#
1
2
3
4
5
6
7
8
9
|
for (int i = 0, j = 0; i < n; i ++ )
{
while (j < i && check(i, j)) j ++ ;
// 具体问题的逻辑
}
常见问题分类:
(1) 对于一个序列,用两个指针维护一段区间
(2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
|
medium#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class Solution {
public:
vector<vector<int> > findContinuousSequence(int sum) {
vector<vector<int> > res;
if(sum==0||sum==1) return res;
vector<int> cur;
int count = 0;
for(int i =1,j=1;i<=sum/2+1;i++){
count+=i;
while(count>sum){
count-=j++;
}
if(count==sum){
for(int k =j;k<=i;k++){
cur.push_back(k);
}
res.push_back(cur);
cur.clear();
}
}
return res;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
class Solution{
public:
//记录每个字符出现的个数
map<char, int>buff;
//y总说了:双指针和单调队列有点相似(狗头
queue<int>q;
void insert(char ch){
//如果buff里面的记录的ch个数为1,并且队列不为空,对头头元素的个数大于1
//队列里面可能会空,但是buff里面一直已经存在的字符
if (++ buff[ch] > 1) {
while (q.size() && buff[q.front()] > 1) q.pop();
} else {
q.push(ch);
}
}
//return the first appearence once char in current stringstream
char firstAppearingOnce(){
if (q.empty()) return '#';
else return q.front();
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char,int> hash;
int res=0;
for(int i=0,j=0;j<s.size();j++)
{
hash[s[j]]++;
while(hash[s[j]]>1)
{
hash[s[i++]]--;
}
res=max(res,j-i+1);
}
return res;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int n =s.length();
unordered_set<char> st;
if(n==0) return 0;
int maxStr = 0,left=0;
for(int i =0;i<n;i++){
while(st.find(s[i])!=st.end()){
st.erase(s[left]);
left++;
}
st.insert(s[i]);
maxStr = max(maxStr,i-left+1);
}
return maxStr;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Solution {
public:
double findMaxAverage(vector<int>& nums, int k) {
double res=-5e4-5.5;
int n = nums.size(),left=0,len=0;
double sum = 0.0;
for(int i =0;i<n;i++){
sum+=nums[i];
len++;
while(len==k){
res=max(res,sum/k);
sum-=nums[left];
left++;
len--;
}
}
return res;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
int left=0,res=n,sum=0;
bool flag =false;
for(int i = 0;i<n;i++){
sum+=nums[i];
while(sum>=target){
res=min(res,i-left+1);
sum-=nums[left];
left++;
flag=true;
}
}
if(flag) return res;
else return 0;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class Solution {
public:
int maximumUniqueSubarray(vector<int>& nums) {
int n = nums.size();
unordered_set<int> st;
int left =0;
int res =0,sum=0;
for(int i =0;i<n;i++){
while(st.find(nums[i])!=st.end()){
st.erase(nums[left]);
sum-=nums[left];
left++;
}
st.insert(nums[i]);
sum+=nums[i];
res=max(res,sum);
}
return res;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> result;
int n = s.length(),m=p.length();
if(n<m) return result;
vector<int> a(26,0);
vector<int> b(26,0);
for(int i =0;i<m;i++) b[p[i]-'a']++;
int left = 0;
for(int i =0;i<n;i++){
a[s[i]-'a']++;
if(i>=m){
a[s[left]-'a']--;
left++;
}
if(a==b) result.push_back(left);
}
return result;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
class Solution {
public:
bool checkInclusion(string s1, string s2) {
int n = s1.length(),m=s2.length();
if(n>m) return false;
vector<int> a(26,0);
vector<int> b(26,0);
for(int i = 0;i<n;i++){
a[s1[i]-'a']++;
}
int left=0;
for(int i =0;i<m;i++){
b[s2[i]-'a']++;
if(i>=n){
b[s2[left]-'a']--;
left++;
}
if(a==b) return true;
}
return false;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int n = nums.size();
deque<int> a;
int res = 0;
for(int i =0;i<n;i++){
while(k==0&&nums[i]==0){
if(a.front()==1){
a.pop_front();
}else{
a.pop_front();
k++;
}
}
if(nums[i]==0){
k--;
}
a.push_back(nums[i]);
int t = a.size();
res=max(res,t);
}
return res;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class Solution {
public:
int equalSubstring(string s, string t, int maxCost) {
int res = 0;
int n = s.length();
int a[n];
vector<int> b(n,0);
for(int i =0;i<n;i++){
a[i]=abs(s[i]-t[i]);
}
int left=0,len=0;
for(int i = 0;i<n;i++){
b[i]=a[i];
maxCost-=a[i];
len++;
while(maxCost<0){
maxCost+=b[left];
left++;
len--;
}
res=max(res,len);
}
return res;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class Solution {
public:
int maxSatisfied(vector<int>& customers, vector<int>& grumpy, int minutes) {
int n = customers.size();
int sum = 0;
int res=0;
vector<int> v;
for(int i =0;i<n;i++){
if(grumpy[i]==0) sum+=customers[i];
else{
v.push_back(i);
}
}
int left = 0;
for(int i =0;i<v.size();i++){
while(v[i]-v[left]>=minutes){
sum-=customers[v[left]];
left++;
}
sum+=customers[v[i]];
res=max(res,sum);
}
res=max(res,sum);
return res;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class Solution {
public:
int maxScore(vector<int>& cardPoints, int k) {
int n = cardPoints.size();
int res = 0,sum=0,left=n-k,len=k;
vector<int> d;
for(int i = 0;i<n;i++){
d.push_back(cardPoints[i]);
}
for(int i = 0;i<n;i++){
d.push_back(cardPoints[i]);
}
for(int i =n-k;i<n+k;i++){
sum+=d[i];
len--;
res=max(res,sum);
if(len==0){
sum-=d[left];
left++;
len++;
}
}
return res;
}
};
|
hard#
思路
总共用到两个哈希表,allWordsMap用于记录words中单词出现的次数,nowWordsMap用于记录子串中(也就是滑动窗口中)单词出现的次数
num为单词的个数,onelen为单词长度
遍历字符串,移动长度为 num* onelen的滑动窗口,再在当前滑动窗口中依次比较onelen长度的单词
当这个窗口内一旦出现不存在allWordsMap中的单词,或者这个单词在子串中出现的次数已经等于allWordsMap中的次数(也就是再加入这个子串次数就要超出了),这个滑动窗口就不符合要求,直接break进入下一个滑动窗口的匹配
一旦完全匹配上了,把滑动窗口的起始索引加入结果res中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
unordered_map<string, int> allWordsMap;
for (auto& v : words) {
allWordsMap[v]++;
}
int num = words.size();
int onelen = words[0].length();
vector<int> res;
if (s.length() < num * onelen) {
return res;
}
for (int left = 0; left < s.length() - num * onelen + 1; ++left)
{
unordered_map<string, int> nowWordsMap;
int right = left;
while (right < left + num * onelen) {
auto cur = s.substr(right, onelen);
if (allWordsMap.find(cur) == allWordsMap.end()
|| nowWordsMap[cur] == allWordsMap[cur]) {
break;
}
++nowWordsMap[cur];
right += onelen;
}
if (right == left + num * onelen) {
res.push_back(left);
}
}
return res;
}
};
|