字节跳动2018算法工程师校招编程题题解(第二批)

编程题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;
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值