尺取法/滑动窗口
滑动窗口是一种常用的算法思想,主要用于解决与数组或字符串相关的子数组或子串问题,尤其是在需要找到满足特定条件的最短或最长子数组/子串时。通过动态调整窗口的左右边界来高效地解决问题,避免了暴力解法中的大量重复计算。
题目:字符串 NC18386
这是一道简单题,即使你还没有学过滑动窗口的算法思想,只要知道运用双指针也应该是可以做的。

代码:
#include<iostream>
#include<string>
using namespace std;
int main(){
string s;
cin>>s;
int zm[26]={0};
int l=0,r=0,count=0,minlen=1e6+5;
while(r<s.size()){
if('a'<=s[r]&&s[r]<='z'){
if(zm[s[r]-'a']==0){
count++;
zm[s[r]-'a']=1;
}else{
zm[s[r]-'a']++;
}
}
while(count==26){
minlen=min(minlen,r-l+1);
if('a'<=s[l]&&s[l]<='z'){
zm[s[l]-'a']--;
if(zm[s[l]-'a']==0){
count--;
}
}
l++;
}
r++;
}
cout<<minlen;
return 0;
}
解题步骤:
1.初始化窗口
-
定义两个指针
left和right,初始时都指向数组或字符串的起始位置。 -
初始化一个数据结构(如哈希表、数组等)来记录窗口内的状态(例如元素的出现次数、满足条件的元素数量等)。
-
初始化一个变量来记录最终结果(如最小长度、最大和等)。
2.扩大窗口
-
移动右指针
right,逐步扩大窗口范围并更新窗口内的状态。 -
检查当前窗口是否满足条件。若不满足条件,继续扩大窗口。
3.缩小窗口
-
如果当前窗口满足条件,尝试通过移动左指针
left来缩小窗口,同时更新窗口内的状态。 -
在缩小窗口的过程中,更新最终结果(例如记录最小长度、最大和等)。
-
当窗口不再满足条件时,停止缩小窗口,返回到扩大窗口的步骤。
4.重复扩大窗口和缩小窗口的过程,直到右指针 right 遍历完整个数组或字符串
题目:连续自然数和 洛谷P1147

代码:
#include<iostream>
using namespace std;
int main(){
int m;
cin>>m;
int l=1,r=1,sum=1;
while(r<=m/2+1){ //一个数拆成两个数,r最大=m/2+1
if(sum<m){
r++;
sum+=r;
}else{
if(sum==m){
cout<<l<<" "<<r<<endl;
}
sum-=l;
l++;
}
}
return 0;
}
题目:A-B数对 洛谷P1102

思路: 先将整数列从小到大排序(给出的整数可能是无序的),每个数A对应的B可能是一个连续区间,如6 2 【1 1 3 3 3 5】,那么我们就要找到满足A的这个连续区间。l指针指向a[l]-a[i]==c的左端点,r指针指向a[r]-a[k]==c的右端+1位置,个数+=r-l,考虑下一个A(>=上一个A)时,区间开始还是上一个区间,避免重复计算
代码:
#include<iostream>
#include<algorithm>
using namespace std;
long long a[200010];
int main(){
int n,c,i;
long long count=0;
cin>>n>>c;
for(i=0;i<n;i++){
cin>>a[i];
}
sort(a,a+n);
int l=1,r=1;
for(i=0;i<n;i++){
while(a[l]-a[i]<c&&l<n){
l++;
}
while(a[r]-a[i]<=c&&r<n){
r++;
}
count+=r-l;
}
cout<<count;
return 0;
}
当然这一题不是特别典型,还可以用方法,我还想到用map容器(自动从小到大排序)统计每个数的个数,再将符合条件的数计算统计个数
代码:
#include<iostream>
#include<map>
using namespace std;
map<long,long>m;
int main(){
int n,c,i;
long long x,count=0;
cin>>n>>c;
for(i=0;i<n;i++){
cin>>x;
m[x]++;
}
for(auto it=m.begin();it!=m.end();it++){
x=it->first+c;
auto jt=m.find(x); //map容器的查找是二分查找,比普通遍历块
if(jt!=m.end()){
count+=it->second*jt->second;
}
}
cout<<count;
return 0;
}
题目:丢手绢 NC207040

思路: 两个小朋友之间的距离不会超过圆周长的一半(定义两个小朋友的距离为沿着圆圈顺时针走或者逆时针走的最近距离),使用双指针,一个指针i在前面走(环状),一个指针j在后面,但距离超过圆周长的一半时,j赶上来,直至j遍历完整圈
代码:
#include<iostream>
using namespace std;
int a[100010];
int main(){
int n,i,j,sum=0,len=0,maxlen=0;
cin>>n;
for(i=0;i<n;i++){
cin>>a[i];
sum+=a[i];
}
i=0,j=0;
while(j<n){
if(len<=sum/2){
maxlen=max(maxlen,len);
len+=a[i];
i++;
i%=n; //环状
}else{
len-=a[j];
j++;
}
}
cout<<maxlen;
return 0;
}
更多推荐
所有评论(0)