字节跳动 2022.3.20后端开发笔试 解答

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、模糊字符串?
  • 二、小明买补给
  • 三、分地区
  • 四、反转字符串
  • 总结


前言

本次的笔试还是比较简单的,我大概用了45分钟左右完成了ak
不过还没有收到面试通知(写完的时候已经收到了嘿嘿嘿)。
所以准备写题解(蒟蒻)来攒一波人品


`提示:本文的代码不是笔试时的通过代码,所以可能有些BUG

一、模糊字符串?

作者:pikeduo
链接:https://nowcoder.net/discuss/868691?type=2&channel=-1&source_id=discuss_tag_discuss_hot_nctrack
来源:牛客网

我们约定"模糊回文字母串"的定义如下: 对于一一个含有英文字母的字符申。
如果满足如下规则: 1)读的时候字母不区分大小写; 2)非字母字符均当作同一字符读叹: 若从左往右读和从右往左读是一样的, 那么就称之为模糊回文字母串。
例如,“a#&A”. “a+b-a”. “abA”. "ab(%BA’均属于模朝回文字母串。 现给定一一个含有字母的字符串s,请编写函数判断字符丰s经过有限次 的字符位置调整后能否变成一个模糊回文字母串,若可以则返true,不 可以则返回false. 输入用例 a#&A 输出 true 输入用例 -A+(AAa)bB 输出 true

题目详情连接
本题非常简单,只需要记录一下每个字符的数量,如果出现两个奇数以上那么就返回false就ok了

#include //牛客是可以用万能头的
using namespace std;
typedef long long ll;
int t,i,j; 
int a[30];

int main(){
	string s;
	cin>>s;
	int b = 0;//记录非字母字符的个数
	
	int len = s.length();
	for(int i=0;i<len;i++){
		if(s[i]>='a' && s[i] <='z')
			a[s[i] - 'a' + 1]++;
		else if(s[i]>='A' && s[i] <='Z')
			a[s[i] - 'A' + 1]++;
		else 
			b++;
	}
	int f = 2;
	
	if(b % 2) f--;
	
	for(int i=0;i<30;i++){
		if(a[i] % 2)f--;
	}
	
	if(f < 1)cout<<"false";
	else cout<<"true";
	
    return 0;    
}


二、小明买补给

作者:pikeduo
链接:https://nowcoder.net/discuss/868691?type=2&channel=-1&source_id=discuss_tag_discuss_hot_nctrack
来源:牛客网

小明想从A徒步到B,总跆程需要M天,路程中为了确保安全,小明每天需 要消耗1份食物。 在起点及路程当中,零星分布着N个朴給站,可以补充食物,不同朴给站 的食物价格可能不同。 请问小明若要安全完成徒步,最少需要花费多少钱呢?
输入描述:
第一行为两个正整教M、N,代表总路程M天,补给站个数N
接下来N行,每行有两个非负整数A. B代表-一个补输站, 表示第A天经 过该补给站,每份食物的价格为B元, A是从0开始严格递增的,即起点一定有补给站,补给站是按位置顺序 给出的,且同一个位置最多有一个补给站。

输出描述: 输出一个整数,表示最少花费的金额

输入
5 4
0 2
1 3
2 1
3 2

输出
7
说明 在第0天买2份食物花费4元,在第2天买3份食物花费3元,共花费7元

题解:显然的贪心题,由于每个加油站的补给数量是无限的,而且可以携带的补给也没有限制(如果有的话,这题将会变得很难),所以我们贪心的考虑只在当前最低价的地方来补充补给,最后一次补充补给时直接购买到终点的量。

#include //牛客是可以用万能头的
using namespace std;
typedef long long ll;
int t,i,j; 
int a[100010],b[100010];//数据范围根据题目来开
int m,n,ta,tb;
int main(){
	cin>>m>>n;
	for(int i=0;i<n;i++){
		cin>>a[i]>>b[i];
	}
	int ans = 0;//答案
	int pos = 0;//代表当前位置
	int price = b[0];//代表当前价格
	
	while(a[pos] < m){
		
		int j = pos+1;
		
		if(j>10)break;
		//while 循环用来找到 后面第一个价格低于当前价格的补给站位置
		while(j < n && b[j] > price) j++;
		
		if(j < n){
			
			ans += price * (a[j] - a[pos]); //在pos处购买补给
			
			pos = j;//来到下一个位置
			
			price = b[j];//更新价格
		}else break;
		
	}
	
	//别忘了从最后一个点到 终点还是需要买补给的
	ans += price * (m - pos);
	
	cout<<ans;
	
    return 0;    
}


三、分地区

作者:pikeduo
链接:https://nowcoder.net/discuss/868691?type=2&channel=-1&source_id=discuss_tag_discuss_hot_nctrack
来源:牛客网

经过500年的大战,X星球的人们在魔法天神的带领下终于消灭了外来物种 Y,然而,长时间的战争使得X军球秋序遭到破坏,人员管理混乱,为了尽 快恢复秩序,建设美好生活乐园,土地如何划分、人员如何管理成为当前 必须要解决的问题。 x星球由多个魔法部落组成,每个魔法部落由多个魔法师组成,每个魔法 师有一个单线联系的来自同一部落的师父

为了公平,X星球上的魔法师之问达成共识,按照每个部满的魔法师人数 比例多少划分土地。 现在,星球上由于长时间大战,魔法师分散星球各地,请你协助魔法天神 按照各部落魔法使人数划分土地。

注意:

1.x星球土地面积共有579万平方干米
2.魔法天神需要把星球的土地全部划分到各个部落中
3.一个部落中的师徒关系有可能出现环,即A是B的师傅,B是C的师 傅, C又是的A师傅

输入描述: .
第一行星球总人数n 和师徒关系数m
(从1、n代表星球上的所有人, 1 (0<=m<=200)
其他m行 ,一共m组师徒关系数据 :徒弟 师傅

输出描述:
第一行 星球总部落数
第二行由多到少每个部落 分得的土地面积(单位为万平力平米,保 留整敫(向下取整>即可)
输入
8 6
1 3
5 3
7 5
6 2
4 8
4 6
输出
2
289 289

说明 8个人,有6个师徒关系,根据师徒关系可以得出 1, 3, 5,7属于一个部落 2,4, 6, 8属于一个部落 所以这个星球共有2个部落, 由于两个部落人数一致,所以平分579万平方千米的土地,即分别 分得209万平方千米

题解:这题目很容易看出来是要求连通块的数量,但是碍于数据范围比较大(具体忘记了,但是最后的20分应该是很大的),所以不能直接暴力求,所以我们需要用到并查集这一数据结构,没用过并查集的可以先百度一下,并查集通常还会运用在求最小生成树等等题目上。

如果能够想到并查集,那么这题也就很容易了

#include //牛客是可以用万能头的
using namespace std;
typedef long long ll;
int t,i,j; 

int f[100010];//数据范围根据题目来开
int peo[100010];//记录每个种族的人数

int m,n,ta,tb;

const int M = 579;//总面积

int find(int x){
	//求父节点的同时进行路径压缩
	return f[x] == x?x:(f[x] = find(f[x]));
}

void init(int x){
	//初始化,每个人的父节点都是自己本身
	for(int i=0;i<=x;i++)f[i] = i;
}

bool cmp(int a,int b){
	return a>b;
}

int main(){
	
	cin>>n>>m;
	init(n);//初始化
	for(int i=0;i<m;i++){
		cin>>ta>>tb;
		int fa = find(ta);
		int fb = find(tb);
		if(fa != fb)//两个人之前不属于同一个部落
		{
			f[fa] = fb;
		}
	}
	//统计部落数
	int ans = 0;
	for(int i=1;i<=n;i++){
		int f = find(i);
		if(f == i){
			ans++;
		}
		peo[f]++; //f部落的人数
	}
	
	cout<<ans<<"\n";
	
	sort(peo+1,peo+1+n,cmp);
	for(int i=1;i<=n;i++){
		if(peo[i] != 0){
			cout<<peo[i] * M / n<<" ";
		}else break;
	}
    return 0;    
}


四、反转字符串

小红拿列了一个长度为n的、仅由大小写字母组成的字符串, 小红有q次操作,每次操作可以选择一个区间, 将该区间的字母大小写翻 转。 请你输出最终的字符串。 输入描述: 第一行输入两个正整数n和q,用空格隔开。代表字符事长度和操 作次数。 第二行输入一个长度为n的、仅由大小写字母组成的字符申。 接下来的q行,每行输入两个正整数l和r,代表小红操作的区 间。 输出描述: 输出最终的字符串。

输入
5 3
aCbqE
1 3
2 5
5 5

输出
ACbQE

说明
第一次操作[1,3]区间,字符串变成AcBqE
第二次操作[2,5] 区间,字符申变成ACbQe
第三次操作[5,5]区间,字符串变成ACbQE

题解:这题如果对于每一次的区间操作都进行暴力修改的话复杂度会来到n*q,显然是过不了的(根据网友的情况来看应该是会得到50分);
那么对于这一题来说,我的解法是用 一个数组来记录 每个位置的修改情况。也就是差分+前缀和
我们可以显然的得到:如果一个位置被修改次数是偶数的话,那么就相当于不需要修改。只有被修改次数为奇数的话才需要修改。

具体代码如下:

#include //牛客是可以用万能头的
using namespace std;
typedef long long ll;
int t,i,j; 
int n,q,l,r;
int a[100010];
string s;

char change(char c){
	if( c <= 'Z' && c>= 'A') return c-'A'+'a';
	else return c-'a'+'A';
}

int main(){
	cin>>n>>q;
	cin>>s;
	for(int i=0;i<q;i++){
		cin>>l>>r;
		//差分数组的修改:从l -> r 这一段的修改次数都+1
		a[l]++;
		a[r+1]--;
		
	}
	for(int i=1;i<=n;i++){
		a[i] += a[i-1];//前缀和
		if(a[i] % 2)//奇数需要修改
		{
			printf("%c",change(s[i-1]));//注意下标问题
		}else{
			printf("%c",s[i-1]);
		}
	}
    return 0;    
}


总结

这次的笔试难度感觉不是很难,但是比较考验同学的算法知识广度(比如说要会写并查集、差分等等)。如果能想到的话,那么都很容易写出来的(模板题吧感觉像)。
这次的题解就写到这了。

祝各位一帆风顺,顺遂安康。

你可能感兴趣的:(笔试,算法,数据结构,贪心算法)