编程题1
题目大意
为了不断优化推荐效果,今日头条每天要存储和处理海量数据。假设有这样一种场景:我们对用户按照它们的注册时间先后来标号,对于一类文章,每个用户都有不同的喜好值,我们会想知道某一段时间内注册的用户(标号相连的一批用户)中,有多少用户对这类文章喜好值为k。因为一些特殊的原因,不会出现一个查询的用户区间完全覆盖另一个查询的用户区间(不存在L1<=L2<=R2<=R1)。
思路
多次区间查询区间内等于x的个数
没有更新,不适用线段树或者树状数组。单纯遍历是O(n),先排序预处理然后二分查找是O(logn),但是排序会破坏区间。所以我们考虑一个区间内的数部分遍历部分二分查找,就是分块的思想,两端遍历,中间每个排序好的块二分查找个数。
AC代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
vector<int>a[1000];
int b[300005];
int n,k,q;
int getp(int x){
return x/k;
}
int query(int l,int r,int c){
int res=0;
int m=getp(l);
int p=getp(r);
for(int i=l;i<(m+1)*k;i++){
if(b[i]==c)res++;
}
for(int i=m+1;i<p;i++){
res+=upper_bound(a[i].begin(),a[i].end(),c)-lower_bound(a[i].begin(),a[i].end(),c);
}
for(int i=p*k;i<=r;i++){
if(b[i]==c)res++;
}
return res;
}
int main(){
scanf("%d",&n);
k=sqrt(n);
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
a[getp(i)].push_back(b[i]);
}
for(int i=0;i<=n/k;i++){
sort(a[i].begin(),a[i].end());
}
scanf("%d",&q);
int l,r,c;
while(q--){
scanf("%d%d%d",&l,&r,&c);
printf("%d\n",query(l,r,c));
}
}
编程题2
题目大意
作为一个手串艺人,有金主向你订购了一条包含n个杂色串珠的手串——每个串珠要么无色,要么涂了若干种颜色。为了使手串的色彩看起来不那么单调,金主要求,手串上的任意一种颜色(不包含无色),在任意连续的m个串珠里至多出现一次(注意这里手串是一个环形)。手串上的颜色一共有c种。现在按顺时针序告诉你n个串珠的手串上,每个串珠用所包含的颜色分别有哪些。请你判断该手串上有多少种颜色不符合要求。即询问有多少种颜色在任意连续m个串珠中出现了至少两次。
思路
比第一题简单得多,滚动区间即可
AC代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
vector<int>a[100005];
int now[55];
int ans[55];
int n,m,c;
int main(){
scanf("%d%d%d",&n,&m,&c);
int x,y;
for(int i=1;i<=n;i++){
scanf("%d",&x);
while(x--){
scanf("%d",&y);
a[i].push_back(y);
}
}
for(int i=1;i<=m;i++){
for(int j=0;j<a[i].size();j++){
now[a[i][j]]++;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=c;j++){
if(now[j]>1)ans[j]=1;
}
for(int j=0;j<a[i].size();j++){
now[a[i][j]]--;
}
if(i+m>n)x=(i+m)%n;
else x=i+m;
for(int j=0;j<a[x].size();j++){
now[a[x][j]]++;
}
}
int res=0;
for(int i=1;i<=c;i++){
if(ans[i]==1)res++;
}
printf("%d",res);
return 0;
}
编程题3
题目大意
字符串S由小写字母构成,长度为n。定义一种操作,每次都可以挑选字符串中任意的两个相邻字母进行交换。询问在至多交换m次之后,字符串中最多有多少个连续的位置上的字母相同?
思路
贪心或者dp,我写的是贪心。
首先理解交换字母,当我们以某个字母为目标时,我们尽量将所有字母向中间靠拢可以使得使用交换次数最小,具体是哪个字母我们需要遍历,每种字母的每个位置作为中间聚集点计算一遍答案求最长距离。
在计算时用到贪心思路,为了使得连续字母数最多,肯定是先将距离聚集点最近的点先交换,所以用两个动态指针向两边扩展,比较两边的距离,选短的进行交换,如此循环即可。
复杂度在O(n2)
AC代码
#include<stdio.h>
#include<string.h>
#include<vector>
#define ll long long
using namespace std;
char s[10005];
int m,n,k,l,r,num,x,left,right,leftp,rightp,ans;
vector<int>pose[30];
int main(){
scanf("%s%d",&s,&m);
n=strlen(s);
for(int i=0;i<n;i++){
pose[s[i]-'a'].push_back(i);
}
ans=0;
for(int i=0;i<=25;i++){
k=pose[i].size();
for(int j=0;j<k;j++){
l=pose[i][j],r=pose[i][j],num=1,x=m;
left=j-1,right=j+1;
leftp,rightp;
while(left>=0&&right<k){
leftp=l-pose[i][left]-1;
rightp=pose[i][right]-r-1;
if(leftp>x&&rightp>x)break;
else if(leftp<rightp){
x-=leftp;
left--;
l--;
num++;
}
else{
x-=rightp;
right++;
r++;
num++;
}
}
while(left>=0){
leftp=l-pose[i][left]-1;
if(leftp>x)break;
x-=leftp;
left--;
l--;
num++;
}
while(right<k){
rightp=pose[i][right]-r-1;
if(rightp>x)break;
x-=rightp;
right++;
r++;
num++;
}
ans=ans>num?ans:num;
}
}
printf("%d",ans);
return 0;
}