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