刷题记录01-美团2020校招后台开发方向笔试题(上)5道编程题

1.表达式求值

给出一个布尔表达式的字符串,比如:true or false and false,表达式只包含true,false,and和or,现在要对这个表达式进行布尔求值,计算结果为真时输出true、为假时输出false,不合法的表达时输出error(比如:true true)。表达式求值是注意and 的优先级比 or 要高,比如:true or false and false,等价于 true or (false and false),计算结果是 true。

输入

输入第一行包含布尔表达式字符串s,s只包含true、false、and、or几个单词(不会出现其它的任何单词),且单词之间用空格分隔。 (1 ≤ |s| ≤ 103).

输出

输出true、false或error,true表示布尔表达式计算为真,false表示布尔表达式计算为假,error表示一个不合法的表达式。

示例

输入例子:
and
输出例子:
error

输入例子:
true and false
输出例子:
false

输入例子:
true or false and false
输出例子:
true

解题思路

先将字符串全部分割,用栈存储,遇到 and 就把栈顶和下一个做一次&, 再把结果入栈,只剩下or, true, false的栈就做|操作,如果只剩一个的时候,判断是or and 就返回 error 是 false true 就输出。

代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <stack>

using namespace std;
int main() {
	string input;
	getline(cin, input);
	stack<string> ret;
	string op;
	int leftnum;
	int rightnum;
	for (int i = 0; i < input.size();) {
		string left;
		string right;
		string tmp;
		while(input[i] != ' ' && i < input.size()) {
			tmp += input[i];
			++i;
		}
		++i;
		if (tmp == "and" && !ret.empty()) {
			left = ret.top();
			ret.pop();
			if (left == "true") { leftnum = 1; }
			else if (left == "false") { leftnum = 0; }
			else {
				cout << "error";
				return 0;
			}
			while (input[i] != ' ' && i < input.size()) {
				right += input[i];
				++i;
			}
			++i;
			if (right == "true") { rightnum = 1; }
			else if (right == "false") { rightnum = 0; }
			else {
				cout << "error";
				return 0;
			}
			tmp = leftnum * rightnum > 0 ? "true" : "false";
			ret.push(tmp);
		}
		else {
			ret.push(tmp);
		}
	}
	while (!ret.empty()) {
		string left;
		string right;
		left = ret.top();
		ret.pop();
		if (ret.empty()) {
			if (left == "and" || left == "or") {
				cout << "error";
			}
			else {
				cout << left;
			}
			return 0;
		}
		if (left == "true") { leftnum = 1; }
		else if (left == "false") { leftnum = 0; }
		else {
			cout << "error";
			return 0;
		}
		op = ret.top();
		ret.pop();
		if(op!="or") {
			cout << "error";
			return 0;
		}
		right = ret.top();
		ret.pop();
		if (right == "true") { rightnum = 1; }
		else if (right == "false") { rightnum = 0; }
		else {
			cout << "error";
			return 0;
		}
		string tmp = leftnum + rightnum > 0 ? "true" : "false";
		ret.push(tmp);
	}
	return 0;
}

代码看着挺乱的,但其实效率还可以。

2.字符串模式匹配

给出两个字符串,分别是模式串P和目标串T,判断模式串和目标串是否匹配,匹配输出 1,不匹配输出 0。模式串中‘?’可以匹配目标串中的任何字符,模式串中的 ’*’可以匹配目标串中的任何长度的串,模式串的其它字符必须和目标串的字符匹配。例如P=a?b,T=acb,则P 和 T 匹配。

输入

输入第一行包含一个字符串p, (1 ≤ |p| ≤ 20).

输入第二行包含一个字符串t, (1 ≤ |t| ≤ 20).

输出

输出仅包含0和1的整数,0表示p和t不匹配,1表示p和t匹配。

示例

输入例子:
a?b
ab
输出例子:
0

输入例子:
a*b
ab
输出例子:
1

输入例子:
a*b
a(cb
输出例子:
1

解题思路

看到字符串匹配我直接哈哈大笑O(∩_∩)O,1A秒了。

代码

#include <regex>
#include <string>
#include <iostream>

using namespace std;

int main() {
	string str;
	string pattern;
	getline(cin, pattern);
	getline(cin, str);

	string mpattern;
	for (int i = 0; i < pattern.size(); ++i) {
		if (pattern[i] == '?') {
			mpattern += '.';
		}
		else if (pattern[i] == '*') {
			mpattern += ".*";
		}
		else {
			mpattern += pattern[i];
		}
	}
	cout << (int)regex_match(str, regex(mpattern.c_str()));
	return 0;
}

3.订单分配

打车派单场景, 假定有N个订单, 待分配给N个司机。每个订单在匹配司机前,会对候选司机进行打分,打分的结果保存在N*N的矩阵A, 其中Aij 代表订单i司机j匹配的分值。假定每个订单只能派给一位司机,司机只能分配到一个订单。求最终的派单结果,使得匹配的订单和司机的分值累加起来最大,并且所有订单得到分配。

输入

第一行包含一个整数N,2≤N≤10。

第二行至第N+1行包含N*N的矩阵。

输入第二行包含一个字符串t, (1 ≤ |t| ≤ 20).

输出

输出分值累加结果和匹配列表,结果四舍五入保留小数点后两位
(注意如果有多组派单方式得到的结果相同,则有限为编号小的司机分配编号小的订单,比如:司机1得到1号单,司机2得到2号单,就比司机1得到2号单,司机2得到1号单要好)

示例

输入例子:
3
1.08 1.25 1.5
1.5 1.35 1.75
1.22 1.48 2.5
输出例子:
5.25
1 2
2 1
3 3
例子说明:
第一行代表得到的最大分值累加结果5.25,四舍五入保留两位小数;

第二行至第四行代表匹配的结果[i j],其中i按行递增:

订单1被派给司机2,订单2被派给司机1,订单3被派给司机3。使得A12+ A21+ A33= 1.25 + 1.5 + 2.5 = 5.25在所有的组合中最大。

解题思路

这个题目我使用回溯来解题,输入是一个二维的float数组,row 是订单,col 是司机,我们从row=0开始(深度),第 0 行的订单被第col(0-n)列的司机接了(广度),那么就把这列设置为已访问(司机踢掉),当前订单总价值就加上这一单的金额,下一行订单不能被已接单的司机进入回溯树了,终止条件就是行满。终止条件还要比较,以第 0 col 为开始的sum集合中最大的总金额,还要记录col 的分布。由此可以写出代码。

代码

#include <iostream>
#include <vector>
using namespace std;

vector<int> ret;    // 坐标位置对应行,值对应列
vector<vector<float>> nums;
void traceback(int row, vector<int>& curCol, vector<bool>& visited, float cursum, float& sum) {
	int n = nums.size();
	if (row == n) {
		if (cursum > sum) {
			sum = cursum;
			ret = curCol;
		}
		return;
	}
	// 列
	for (int col = 0; col < n; ++col) {
		if (!visited[col]) {
			visited[col] = true;
			cursum += nums[row][col];
			curCol[row] = col;
			traceback(row+1, curCol, visited, cursum, sum);
			cursum -= nums[row][col];
			visited[col] = false;
		}
	}
}
int main() {
	int n;
	cin >> n;
	nums.resize(n, vector<float>(n));
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < n; ++j) {
			cin >> nums[i][j];
		}
	}

	float sum = -1;
	float curSum = 0;
	vector<int> curCol(n);
	vector<bool> isvisited(n, false);
	traceback(0, curCol, isvisited, curSum, sum);

	printf("%.2f\n", sum);
	for (int i = 0; i < ret.size(); ++i) {
		cout << i + 1 << " " << ret[i] + 1 << endl;
	}

	return 0;
}

4.美团骑手包裹区间分组

2110年美团外卖火星第3000号配送站点有26名骑手,分别以大写字母A-Z命名,因此可以称呼这些骑手为黄家骑士特工A,黄家骑士特工B…黄家骑士特工Z,某美团黑珍珠餐厅的外卖流水线上会顺序产出一组包裹,美团配送调度引擎已经将包裹分配到骑手,并在包裹上粘贴好骑手名称,如RETTEBTAE代表一组流水线包裹共9个,同时分配给了名字为A B E R T的5名骑手。请在不打乱流水线产出顺序的情况下,把这组包裹划分为尽可能多的片段,同一个骑手只会出现在其中的一个片段,返回一个表示每个包裹片段的长度的列表。

输入

输入数据只有一行,为一个字符串(不包含引号),长度不超过1000,只包含大写字母’A’到’Z’,字符之间无空格。

输出

输出每个分割成片段的包裹组的长度,每个长度之间通过空格隔开

示例

输入例子:
MPMPCPMCMDEFEGDEHINHKLIN
输出例子:
9 7 8
例子说明:
划分结果为MPMPCPMCM,DEFEGDE,HINHKLIN。

每个骑手最多出现在一个片段中。

像MPMPCPMCMDEFEGDE,HINHKLIN的划分是错误的,因为划分的片段数较少。

解题思路

第一个字符Cb,在字符串中最后一次出现的位置Ce,这串字符中包含的字符,也要找最后一次出现的位置。所以先遍历一遍字符串,用两个数组,第一个数组记录每个字符串第一次出现的位置,第二个数组记录每个字符串最后一次出现的位置。因为是记录 A-Z 所以 数组大小 26 即可。使用一个数组保存字符最后的位置,只有第一次的保存,其他的置零。于是可以循环更新串中最大位置前的最大位置,如果不用更新,就记录最大位置与初始位置之差加一,就是分割的子串大小。

代码

int main() {
	string input;
	getline(cin, input);
	int fpos[26];
	int epos[26];
	int once[26] = {0};
	int* lpos = (int*)calloc(input.size(), sizeof(int));
	for (int i = 0; i < 26; ++i) {
		fpos[i] = epos[i] = -1;
	}
	for (int i = 0; i < input.size(); ++i) {
		if (fpos[input[i]- 'A'] == -1) {
			fpos[input[i] - 'A'] = i;
		}
	}
	for (int j = input.size() - 1; j >= 0; --j) {
		if (epos[input[j] - 'A'] == -1) {
			epos[input[j] - 'A'] = j;
		}
	}
	// 第一次出现的时候,变成最后一次出现的位置的值,后面再出现就为0
	for (int i = 0; i < input.size(); ++i) {
		// 第一次
		if (once[input[i] - 'A'] == 0) {
			lpos[i] = epos[input[i] - 'A'];
			once[input[i] - 'A'] = 1;
		}
	}
	//for (int i = 0; i < input.size(); ++i) {
	//	cout << lpos[i] << " ";
	//}
	//cout << endl;
	vector<int> ret;
	for (int i = 0; i < input.size(); ) {
		int last = lpos[i];
		int j = last;
		while (j > i) {
			if (lpos[j] > last) {
				last = j = lpos[j];
			}
			else {
				--j;
			}
		}
		ret.push_back(last - i + 1);
		i = last+1;
	}
	
	for (int i = 0; i < ret.size(); ++i) {
		cout << ret[i] << " ";
	}

	return 0;
}

算是相对前面的题目而言比较简单的题目了,但是也写了好久。

5.火星文字典

已知一种新的火星文的单词由英文字母(仅小写字母)组成,但是此火星文中的字母先后顺序未知。给出一组非空的火星文单词,且此组单词已经按火星文字典序进行好了排序(从小到大),请推断出此火星文中的字母先后顺序。

输入

一行文本,为一组按火星文字典序排序好的单词(单词两端无引号),单词之间通过空格隔开

输出

按火星文字母顺序输出出现过的字母,字母之间无其他字符,如果无法确定顺序或者无合理的字母排序可能,请输出"invalid"(无需引号)

示例

输入例子:
z x
输出例子:
zx

输入例子:
wrt wrf er ett rftt
输出例子:
wertf

输入例子:
z x z
输出例子:
invalid

解题思路

力扣269 ,不是会员的我眼泪掉下来。看了看题解,先根据词排序得到字母的有向无环图,再求图的拓扑序列。我(キ`゚Д゚´)!!???什么东西?

代码

#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <map>

using namespace std;

map<char, set<char>> mp;
map<char, int> vis;
vector<string> v;
set<char> st;
string res;

bool dfs(char c) {
    vis[c] = -1;
    for (auto i : mp[c]) if (vis[i] != 1) {
        if (vis[i] == -1) return false;
        if (!dfs(i)) return false;
    }
    vis[c] = 1;
    res = c + res;
    return true;
}

void f(int i, int j, int k) {
    if (i < j && v[i].size() <= k) ++i;
    if (i == j) return;
    char c = v[i][k];
    int ii = i;
    for (int t = i; t <= j; ++t) {
        if (v[t][k] != c) {
            mp[c].insert(v[t][k]);
            st.erase(v[t][k]);
            f(ii, t - 1, k + 1);
            ii = t;
            c = v[t][k];
        }
        else if (t == j) f(ii, t, k + 1);
    }
}
int main() {
	string input;
	getline(cin, input);
	for(int i=0; i<input.size(); ){
		string tmp;
		while(input[i] != ' ' && i < input.size()) {
			tmp += input[i];
			++i;
		}
		++i;
		for (auto c : tmp) st.insert(c);
        v.emplace_back(std::move(tmp));
	}

    f(0, v.size() - 1, 0);
    if (st.size() != 1 || !dfs(*st.begin())) {
        cout << "invalid\n";
        return 0;
    }
	cout << res << endl;
	return 0;
}

鏖战了5个多小时,自己做了四题,最后一题看题解都看不懂༼༎ຶᴗ༎ຶ༽。
还是自己水平太差,要多思考多练习,相信依靠坚持和努力,肯定可以去美团送外卖的。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值