目录
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;
}