蓝桥杯-双指针专题(包含历年蓝桥杯真题和详细注释)

目录

1、字符串删减

 2、最长连续不重复子序列

3、数组元素的目标和 

4、判断子序列 

5、日志统计(第九届蓝桥杯省赛C++B组,第九届蓝桥杯省赛JAVAB组) 

 6、1240. 完全二叉树的权值(第十届蓝桥杯省赛C++A/B组,第十届蓝桥杯省赛JAVAA组)


1、字符串删减

给定一个由 nn 个小写字母构成的字符串。

现在,需要删掉其中的一些字母,使得字符串中不存在连续三个或三个以上的 x

请问,最少需要删掉多少个字母?

如果字符串本来就不存在连续的三个或三个以上 x,则无需删掉任何字母。

输入格式

第一行包含整数 nn。

第二行包含一个长度为 nn 的由小写字母构成的字符串。

输出格式

输出最少需要删掉的字母个数。

数据范围

3≤n≤1003≤n≤100

输入样例1:

6
xxxiii

输出样例1:

1

输入样例2:

5
xxoxx

输出样例2:

0

输入样例3:

10
xxxxxxxxxx

输出样例3:

8
#include <iostream>
using namespace std;
int main()
{
    int n;string s;
    cin>>n>>s;
    int ans=0;//思路就是找到 所有x的区间 用[l,r]维护区间
    for(int l=0,r=0;l<n;l++)
    {
        if(s[l]=='x'){
            r=l;
            while(r+1<n&&s[r+1]=='x')r++;//开始往后遍历 直到区间包含不是x的字符
            ans+=max(0,r-l-1);//(r-l+1-2)简化而来
            //因为不得连续三个或三个以上 那么只要大于等于3个 都必须-1 例如4个 4-2 五个5-3 保证连续不大于等于三个
            l=r;//因为等一下会循环进行l++ 因此这边设置成l=r 等一下就是r+1 非字符x 继续寻找下一个区间
        }
    }
    cout<<ans<<endl;
    return 0;
}

 2、最长连续不重复子序列

给定一个长度为 nn 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。

输入格式

第一行包含整数 nn。

第二行包含 nn 个整数(均在 0∼1050∼105 范围内),表示整数序列。

输出格式

共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。

数据范围

1≤n≤1051≤n≤105

输入样例:

5
1 2 2 3 5

输出样例:

3
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 1e5+10;
int a[N],cnt[N];//使用来a存储题目的数组 cnt来计算每一个数字在当前子序列的个数 
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)cin>>a[i];
    int ans=0;
    for(int l=0,r=0;r<n;r++)//l为左指针 r为右指针 维护一个[l,r]的子序列区间
    {
       cnt[a[r]]++;//将当前a[r]进行加一操作 即当前子序列的a[r]个数+1
       while(cnt[a[r]]>1)cnt[a[l++]]--;//当右边界大于1时说明当前维护的区间有重复的字符 这时候移动左指针 直到没有重复的字符
       ans=max(ans,r-l+1);        //计算答案 
    }
    cout<<ans<<endl;
    return 0;
}

3、数组元素的目标和 

给定两个升序排序的有序数组 AA 和 BB,以及一个目标值 xx。

数组下标从 00 开始。

请你求出满足 A[i]+B[j]=xA[i]+B[j]=x 的数对 (i,j)(i,j)。

数据保证有唯一解。

输入格式

第一行包含三个整数 n,m,xn,m,x,分别表示 AA 的长度,BB 的长度以及目标值 xx。

第二行包含 nn 个整数,表示数组 AA。

第三行包含 mm 个整数,表示数组 BB。

输出格式

共一行,包含两个整数 ii 和 jj。

数据范围

数组长度不超过 105105。
同一数组内元素各不相同。
1≤数组元素≤1091≤数组元素≤109

输入样例:

4 5 6
1 2 4 7
3 4 6 8 9

输出样例:

1 1
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int a[N],b[N];
int main()
{
    int n,m,x;
    cin>>n>>m>>x;
    for(int i=0;i<n;i++)cin>>a[i];
    for(int j=0;j<m;j++)cin>>b[j];
    for(int l=0,r=m-1;l<n;l++)
    {//维护一个左右区间 因为数组都是有序的 a从左边开始走 如果比x小 就会一直走 使得直到比x大或和x相等
        while(a[l]+b[r]>x)r--;//当结果比x大 那么就需要b的r指针开始向左边移动 使我们靠答案更近
        if(a[l]+b[r]==x){
            cout<<l<<' '<<r<<endl;
            break;//因为答案唯一解 遍历到就可以退出
        }
    }
    return 0;
}

4、判断子序列 

给定一个长度为 nn 的整数序列 a1,a2,…,ana1,a2,…,an 以及一个长度为 mm 的整数序列 b1,b2,…,bmb1,b2,…,bm。

请你判断 aa 序列是否为 bb 序列的子序列。

子序列指序列的一部分项按原有次序排列而得的序列,例如序列 {a1,a3,a5}{a1,a3,a5} 是序列 {a1,a2,a3,a4,a5}{a1,a2,a3,a4,a5} 的一个子序列。

输入格式

第一行包含两个整数 n,mn,m。

第二行包含 nn 个整数,表示 a1,a2,…,ana1,a2,…,an。

第三行包含 mm 个整数,表示 b1,b2,…,bmb1,b2,…,bm。

输出格式

如果 aa 序列是 bb 序列的子序列,输出一行 Yes

否则,输出 No

数据范围

1≤n≤m≤1051≤n≤m≤105,
−109≤ai,bi≤109−109≤ai,bi≤109

输入样例:

3 5
1 3 5
1 2 3 4 5

输出样例:

Yes
#include <iostream>
using namespace std;

const int N = 1e5+10;
int a[N],b[N];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;i++)cin>>a[i];
    for(int i=0;i<m;i++)cin>>b[i];
    //用l指针遍历b区间      r指针遍历a区间
    int l=0,r=0;
    for(;l<m&&r<n;l++)
    {
        if(a[r]==b[l])r++;//只有b[l]等于a[r]  r才会进行++  同时也保证顺序性
    }
    if(r==n)cout<<"Yes"<<endl;//当r==n时说明 所有元素都在b区间内
    else cout<<"No"<<endl;
    return 0;
    
}

5、日志统计(第九届蓝桥杯省赛C++B组,第九届蓝桥杯省赛JAVAB组) 

小明维护着一个程序员论坛。现在他收集了一份”点赞”日志,日志共有 NN 行。

其中每一行的格式是:

ts id  

表示在 tsts 时刻编号 idid 的帖子收到一个”赞”。

现在小明想统计有哪些帖子曾经是”热帖”。

如果一个帖子曾在任意一个长度为 DD 的时间段内收到不少于 KK 个赞,小明就认为这个帖子曾是”热帖”。

具体来说,如果存在某个时刻 TT 满足该帖在 [T,T+D)[T,T+D) 这段时间内(注意是左闭右开区间)收到不少于 KK 个赞,该帖就曾是”热帖”。

给定日志,请你帮助小明统计出所有曾是”热帖”的帖子编号。

输入格式

第一行包含三个整数 N,D,KN,D,K。

以下 NN 行每行一条日志,包含两个整数 tsts 和 idid。

输出格式

按从小到大的顺序输出热帖 idid。

每个 idid 占一行。

数据范围

1≤K≤N≤1051≤K≤N≤105,
0≤ts,id≤1050≤ts,id≤105,
1≤D≤100001≤D≤10000

输入样例:

7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3

输出样例:

1
3
#include <iostream>
#include <cstring>
#include <algorithm>
#define x first
#define y second
using namespace std;
const int N = 1e5+10;
typedef pair<int, int> PII;
PII logs[N];//存下输入的日志
int cnt[N];//cnt[i]当前区间内i号帖子被访问的次数
bool st[N];//是否是热帖

int main()
{
    int n,d,k;
    cin>>n>>d>>k;
    for(int i=0;i<n;i++)cin>>logs[i].x>>logs[i].y;
    sort(logs,logs+n);//对帖子点赞按时间进行排序
    for(int r=0,l=0;r<n;r++)//维护时间[l,r)区间内的点赞数量
    {
        cnt[logs[r].y]++;//当前帖子数量+1
        while(logs[r].x-logs[l].x>=d){//当(logs[r].x-logs[l].x>=d 代表 当前时间区间已经不符合条件 因此开始维护 [l+1,r+1)区间
            cnt[logs[l].y]--;//将l区间的计数进行扣除
            l++;//让左指针往前走
        }
        if(cnt[logs[r].y]>=k)st[logs[r].y]=true;//只要一次 在区间内部帖子点赞数大于等于k 即是热帖 做个标识 
    }
    
    for(int i=0;i<100000;i++)if(st[i])cout<<i<<endl;//按照标识 遍历输出答案 100000是数据范围得来
    return 0;
    
    
}

 6、1240. 完全二叉树的权值(第十届蓝桥杯省赛C++A/B组,第十届蓝桥杯省赛JAVAA组)

给定一棵包含 NN 个节点的完全二叉树,树上每个节点都有一个权值,按从上到下、从左到右的顺序依次是 A1,A2,⋅⋅⋅ANA1,A2,···AN,如下图所示:

现在小明要把相同深度的节点的权值加在一起,他想知道哪个深度的节点权值之和最大?

如果有多个深度的权值和同为最大,请你输出其中最小的深度。

注:根的深度是 11。

输入格式

第一行包含一个整数 NN。

第二行包含 NN 个整数 A1,A2,⋅⋅⋅ANA1,A2,···AN。

输出格式

输出一个整数代表答案。

数据范围

1≤N≤1051≤N≤105,
−105≤Ai≤105−105≤Ai≤105

输入样例:

7
1 6 5 4 3 2 1

输出样例:

2

#include<iostream>
using namespace std;
typedef long long LL;//一定要开 longlong 我就是没开longlong wa了好几发
const int N = 1e5+10;
int a[N];
int main()
{
    int n;
    cin >> n;
    int ans=0;LL sum=-1e18;//ans表示答案 sum表示当前计入的深度权值和最大值
    int dep=1,cnt=1;//使用dep来表示层数(或者叫做深度) cnt表示当前那一层的节点数
    for(int i=1;i<=n;i++)cin>>a[i];
    
    
    for(int i=1;i<=n;)
    {
        LL temp=0;//临时计入当前层数的深度权值和
        for(int j=i,t=0;t<cnt&&i<=n;j++,i++,t++)//进行遍历 从i开始 遍历cnt次  这边写的比较复杂 懒得优化 其实不需要这么多变量
        {
            temp+=a[j];//进行累加操作 当前的深度权值和
        }
        if(temp>sum){ans=dep; sum=temp;}//如果当前权值和大于 已经记录的最大权值和 那就进行更新 并且将ans等于当前深度dep
        dep++;cnt*=2;//深度加1 二叉树的性质 叶子结点*2
    }
    cout<<ans<<endl;
    return 0;
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值