c++算法基础必刷题目——尺取法

尺取法

  尺取法通常也叫滑动窗口法,顾名思义,像尺子一样取一段,借用挑战书上面的话说,尺取法通常是对数组保存一对下标,即所选取的区间的左右端点,然后根据实际情况不断地推进区间左右端点以得出答案。尺取法比直接暴力枚举区间效率高很多,尤其是数据量大的时候,所以说尺取法是一种高效的枚举区间的方法,是一种技巧,一般用于求取有一定限制的区间个数或最短的区间等等

1、字符串

NC18386 字符串

题目描述
  小N现在有一个字符串S。他把这这个字符串的所有子串都挑了出来。一个S的子串T是合法的,当且仅当T中包含了所有的小写字母。小N希望知道所有的合法的S的子串中,长度最短是多少。

输入描述:
一行一个字符串S。只包含小写字母。S的长度不超过106.

输出描述:
一行一个数字,代表最短长度。数据保证存在一个合法的S的子串。

示例1
输入
ykjygvedtysvyymzfizzwkjamefxjnrnphqwnfhrnbhwjhqcgqnplodeestu

输出
49

解题思路:
1、通过滑动窗口记录不同字符的个数,当包含了26个不同字符时缩小窗口,直到恰好不包括26个不同字符时停止,取最优解

2、详细的步骤在代码中有解释

代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
    string s;
    cin>>s;
    int a[26]={0};//a[0]代表字符a的个数,a[1]代表字符b的个数,依次下去
    int j=0;//用来滑动窗口
    int ans=INT_MAX;//一个足够大的值即可
    int sum=0;//sum代表区间里不同字符的个数
    for(int i=0;i<s.size();i++){
        a[s[i]-'a']++;
        if(a[s[i]-'a']==1){//如果个数从0变为1,那么区间不同字符数+1
            sum++;
        }
        while(sum==26){//如果区间不同字符数等于26,代表所有字符都包含
            a[s[j]-'a']--;
            if(a[s[j]-'a']==0){
                sum--;
                ans=min(ans,i-j+1);//取最优解
            }
            j++;
        }
    }
    cout<<ans<<endl;
}

2、丢手绢

NC207040 丢手绢

题目描述
  “丢~丢~丢手绢,轻轻地放在小朋友的后面,大家不要告诉她,快点快点抓住她,快点快点抓住她。”

  牛客幼儿园的小朋友们围成了一个圆圈准备玩丢手绢的游戏,但是小朋友们太小了,不能围成一个均匀的圆圈,即每个小朋友的间隔可能会不一致。为了大家能够愉快的玩耍,我们需要知道离得最远的两个小朋友离得有多远(如果太远的话牛老师就要来帮忙调整队形啦!)。

  因为是玩丢手绢,所以小朋友只能沿着圆圈外围跑,所以我们定义两个小朋友的距离为沿着圆圈顺时针走或者逆时针走的最近距离。

输入描述:
第一行一个整数N,表示有N个小朋友玩丢手绢的游戏。
接下来的第2到第n行,第i行有一个整数,表示第i-1个小朋友顺时针到第i个小朋友的距离。
最后一行是第N个小朋友顺时针到第一个小朋友的距离。

输出描述:
输出一个整数,为离得最远的两个小朋友的距离。

示例1
输入
3
1
2
3

输出
3

备注:
2≤N≤100000
距离和(圆圈周长)小于等于2147483647

解题思路:
这是一个环型的尺取问题,从哪里开始都没有问题,最终全部判断完即可,下面解释样例:
在这里插入图片描述

1、开始,l和r都在a点,目前不是最优状态,r往顺时针方向走到b,目前也不是最优状态,r往顺时针方向走到c,达到最优状态,记录答案

2、a点达到最优状态后往顺时针方向走到b,此时判断r是否是最优状态,可以发现r只会往顺时针方向走

3、依次判断完所有的点的最优状态,取最优解即可

代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int a[100010]={0};
    int n;
    cin>>n;
    int sum1=0,sum2=0;
    for(int i=0;i<n;i++){
        cin>>a[i];
        sum2+=a[i];
    }
    int j=0;
    int ans=0;
    for(int i=0;i<n;i++){
        if(i){
            sum1-=a[i-1];
            sum2+=a[i-1];
        }
        while(sum2-a[j]>sum1+a[j]){
            sum2-=a[j];
            sum1+=a[j];
            j++;
        }
        ans=max(ans,min(sum1,sum2));
        ans=max(ans,min(sum1+a[j],sum2-a[j]));
    }
    cout<<ans<<endl;
    
}

是不是很简单呢?

刚接触肯定会觉得难,多些做题多些用,熟悉了就容易了,兄弟萌,加油!!!

文章尚有不足,欢迎大牛们指正

感谢观看,点个赞吧

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旧林墨烟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值