美团点评2020校招后台开发方向笔试题 练习解题报告 Apare_xzc
美团点评2020校招后台开发方向笔试题
2020.8.6
题目牛客链接:美团点评2020校招后台开发方向笔试题
5/9 [编程题]表达式求值
给出一个布尔表达式的字符串,比如: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。
分析:类似中缀表达式求值。一个操作数栈存操作数(true/false),一个操作符栈存操作符(and/or) 因为and的优先级较高,所以在or入栈时要先把前面所有and弹栈计算。
#include <bits/stdc++.h>
using namespace std;
vector<string> v;
char str[100000];
string cal(string x,string y,string op) {
if(op=="and") return (x=="true"&&y=="true")?"true":"false";
else return (x=="true"||y=="true")?"true":"false";
}
int main(void) {
cin.getline(str,10000);
string ss;
int len = strlen(str);
int i = 0;
while(str[i]==' ') ++i;
for(;i<=len;++i) {
if(str[i]==' '||str[i]==0) v.push_back(ss),ss="";
else ss += str[i];
}
stack<string> num;
stack<string> op;
for(i=0;i<(int)v.size();++i) {
if(v[i]=="true"||v[i]=="false") num.push(v[i]);
else if(v[i]=="and") {
op.push(v[i]);
}
else {
while(!op.empty()&&op.top()=="and") {
string tp1,tp2;
if(num.empty()) {
puts("error");return 0;
}
tp1 = num.top();num.pop();
if(num.empty()) {
puts("error");return 0;
}
tp2 = num.top();num.pop();
num.push(cal(tp1,tp2,op.top()));
op.pop();
}
op.push(v[i]);
}
}
while(!op.empty()) {
string tp1,tp2;
if(num.empty()) {
puts("error");return 0;
}
tp1 = num.top();num.pop();
if(num.empty()) {
puts("error");return 0;
}
tp2 = num.top();num.pop();
num.push(cal(tp1,tp2,op.top()));
op.pop();
}
if(num.empty()) {
puts("error");return 0;
}
string ans = num.top();
num.pop();
if(num.empty()) cout<<ans<<endl;
else puts("error");
return 0;
}
6/9 [编程题]字符串模式匹配
给出两个字符串,分别是模式串P和目标串T,判断模式串和目标串是否匹配,匹配输出 1,不匹配输出 0。模式串中‘?’可以匹配目标串中的任何字符,模式串中的 ’*’可以匹配目标串中的任何长度的串,模式串的其它字符必须和目标串的字符匹配。例如P=a?b,T=acb,则P 和 T 匹配。
分析:
因为字符串长度不大于20,所以我们可以进行O(n^2)的dp. dp[i][j]
代表模式串P的前i位和目标串T的前j位是否匹配。我们知道,?
可以代表任意一个字符,*
可以代表任意长度的字符串(包括空串)。
- 如果P[i]是
?
, 那么dp[i][j] = dp[i-1][j-1]
- 如果P[i]是
*
,那么dp[i][j] = dp[i-1][j] || dp[i][j-1] || dp[i-1][j]
(ab*,abc)(ab*,ab)(ab*,abcde) - 如果P[i]不是通配符,那么
dp[i][j] = dp[i-1][j-1]&&P[i]==T[j]
- 初始化操作:
dp[0][0] = true, dp[i][0] = dp[i-1][0] && P[i]=='*'
,只有都是*
才能和空串匹配
#include <bits/stdc++.h>
using namespace std;
bool dp[100][100];
char p[100],t[100];
int main(void) {
cin>>(p+1)>>(t+1);
int len1 = strlen(p+1);
int len2 = strlen(t+1);
dp[0][0] = true;
for(int i=1;i<=len1;++i) {
dp[i][0] = dp[i-1][0]&&p[i]=='*';
}
for(int i=1;i<=len1;++i) {
for(int j=1;j<=len2;++j) {
if(p[i]=='?'||p[i]==t[j]) dp[i][j] = dp[i-1][j-1];
else if(p[i]=='*') dp[i][j] = dp[i-1][j]||dp[i][j-1]||dp[i-1][j-1];
}
}
if(dp[len1][len2]) puts("1");
else puts("0");
return 0;
}
7/9 [编程题]订单分配
打车派单场景, 假定有N个订单, 待分配给N个司机。每个订单在匹配司机前,会对候选司机进行打分,打分的结果保存在N*N的矩阵A, 其中Aij 代表订单i司机j匹配的分值。
假定每个订单只能派给一位司机,司机只能分配到一个订单。求最终的派单结果,使得匹配的订单和司机的分值累加起来最大,并且所有订单得到分配。
分析:dfs然后记录最佳答案即可
#include <bits/stdc++.h>
using namespace std;
const int N = 12;
double a[N][N];
double res = -1000;
int r[N],n;
int g[N];
bool vis[N];
void solve() {
double ans = 0.0;
for(int i=1;i<=n;++i)
ans += a[i][r[i]];
if(ans>res) {
res = ans;
for(int i=1;i<=n;++i) g[i] = r[i];
}
}
void dfs(int x) {
if(x==n+1) {
solve();return;
}
for(int i=1;i<=n;++i) {
if(!vis[i]) r[x]=i,vis[i]=true,dfs(x+1),vis[i]=false;
}
}
int main(void) {
cin>>n;
for(int i=1;i<=n;++i) {
for(int j=1;j<=n;++j)
cin>>a[i][j];
}
dfs(1);
printf("%.2f\n",res);
for(int i=1;i<=n;++i) cout<<i<<" "<<g[i]<<endl;
return 0;
}
8/9 [编程题]美团骑手包裹区间分组
2110年美团外卖火星第3000号配送站点有26名骑手,分别以大写字母A-Z命名,因此可以称呼这些骑手为黄家骑士特工A,黄家骑士特工B…黄家骑士特工Z,某美团黑珍珠餐厅的外卖流水线上会顺序产出一组包裹,美团配送调度引擎已经将包裹分配到骑手,并在包裹上粘贴好骑手名称,如RETTEBTAE代表一组流水线包裹共9个,同时分配给了名字为A B E R T的5名骑手。请在不打乱流水线产出顺序的情况下,把这组包裹划分为尽可能多的片段,同一个骑手只会出现在其中的一个片段,返回一个表示每个包裹片段的长度的列表。MPMPCPMCMDEFEGDEHINHKLIN
分析:
- 可以统计出每个字母最后一次出现的位置,记为ed[ch]
- 维护left指针和right指针,从前往后遍历,当前字符为ch,用ed[ch]更新right。直到
i==right
,则可以切割,贪心遍历即可。
#include <bits/stdc++.h>
using namespace std;
//MPMPCPMCMDEFEGDEHINHKLIN
char str[10000];
int st[300],ed[300]; //st[]其实没有用到
int main(void) {
scanf("%s",str);
memset(st,-1,sizeof(st));
int i;
for(i=0;str[i];++i) {
int ch = str[i];
if(st[ch]==-1) st[ch] = i;
else ed[ch] = i;
}
int len = i;
vector<int> v;
int left = 0,right = 0;
for(i=0;i<len;++i) {
right = max(right,ed[str[i]]);
if(i==right) {
v.push_back(right-left+1);
left = i+1;
right = i+1;
}
}
for(auto x:v)
cout<<x<<" ";cout<<endl;
return 0;
}
9/9 [编程题]火星文字典
已知一种新的火星文的单词由英文字母(仅小写字母)组成,但是此火星文中的字母先后顺序未知。给出一组非空的火星文单词,且此组单词已经按火星文字典序进行好了排序(从小到大),请推断出此火星文中的字母先后顺序。wrt wrf er ett rftt
wertf
分析:
- 字典序的概念我们知道,是从头开始比较,只要两个字符串相同位置的字符不同,我们就可以比出来优先级。
- 然后我们可以按照按照字符优先级建立有向图,然后拓扑排序。如果不存在环并且拓扑序唯一,则输出拓扑序,否则输出invaild
- 如果拓扑排序的时候入度为0的结点不止一个,那么无法确定顺序
- 如果拓扑排序之后没有访问到所有n个结点,说明有环
#include <bits/stdc++.h>
using namespace std;
vector<string> v;
vector<int> edge[300];
int du[300];
map<pair<int,int>,int> mp;
set<char> st;
void addedge(int x,int y) {
pair<int,int> pr(x,y);
if(mp.count(pr)) return;
mp[pr] = 1;
edge[x].push_back(y);
du[y]++;
}
void cal(string x,string y) {
int lenx = x.length();
int leny = y.length();
int i = 0, j = 0;
while(i<lenx&&j<leny) {
if(x[i]==y[j]) ++i,++j;
else {
addedge(x[i],y[j]);
break;
}
}
}
int main(void) {
string s;
int n = 0;
while(cin>>s) {
++n;
v.push_back(s);
for(auto ch:s)
st.insert(ch);
}
for(int i=0;i<n-1;++i) {
cal(v[i],v[i+1]);
}
int cnt = 1;
queue<int> Q;
int num = 0;
for(auto x:st) {
++num;
if(du[x]==0) Q.push(x);
}
string res;
while(!Q.empty()) {
if(Q.size()!=1) {
puts("invalid");return 0;
}
int tp = Q.front();Q.pop();
res += tp;
int len = edge[tp].size();
for(int i=0;i<len;++i) {
int to = edge[tp][i];
du[to]--;
if(du[to]==0) Q.push(to);
}
}
if(res.length()==num) cout<<res<<endl;
else puts("invalid");
return 0;
}
问答题及答案
- RESTful调用和 RPC调用有什么区别?如果让你设计一个RPC服务治理框架你会设计那些模块?是否了解过Service Mesh,如果了解Service Mesh是用来解决什么问题的?
1、RESTful是一种软件架构风格,用于约束客户端和服务器交互,满足这些约束条件和原则的应用程序或设计就是 RESTful。比如HTTP协议使用同一个URL地址,通过GET,POST,PUT,DELETE等方式实现查询、提交、删除数据。RPC是远程过程调用,是用于解决分布式系统服务间调用的一种方式。RPC采用客户端与服务端模式,双方通过约定的接口(常见为通过IDL定义或者是代码定义)以类似本地方法调用的方式来进行交互,客户端根据约定传输调用函数+参数给服务端(一般是网络传输TCP/UDP),服务端处理完按照约定将返回值返回给客户端。
重点为RESTful HTTP的约束风格,RPC调用模型。
1、可分为两大部分RPC +服务治理
RPC部分 = IDL +客户端/服务端实现层 +协议层 +数据传输层
服务治理 =服务管理(注册中心) +服务监控 +服务容灾 +服务鉴权
2、 Service Mesh为了解决传统微服务框架"胖客户端"方式,引入的如下问题:
与业务无关的服务治理逻辑与业务代码强耦合,框架、SDK的升级与业务代码强绑定,多语言的胖客户端支持起来性价比极低。
- 请描述避免多线程竞争时有哪些手段?
- 不可变对象;
- 互斥锁;
- ThreadLocal 对象;
- CAS;
- 临界区:通过对多线程的串行化来访问公共资源,速度快,适合控制数据访问
- 互斥量:在对内存进行读写访问时给访问资源上锁,此时其他线程无法对上锁资源进行处理
- 信号量:控制剧有限数量用户资源而设计,通过一个计数器减操作完成对资源的访问,然后转交给其他线程,另外线程的计数器在通信线程减操作
的同时进行加操作,通过计数器实现通信- 条件变量:通过阻塞,和通知解除阻塞来避免竞争
- 读写锁: 读时不上锁,写时上锁,如果资源允许脏读会比较合适
- 原子锁, 通过原子操作, Interlocked.CompareExchange实现“无锁”竞争
7.原子性操作, 野外原子性操作天生线程安全,不需要加所
- 请简述HTTP的5个常用Method及其含义,以及5个常用Status Code及其含义?HTTP与HTTPS的区别是什么,简述一下HTTPS的实现原理。
链接:https://www.nowcoder.com/questionTerminal/92a370c61977486495caf2c916dd09ec
来源:牛客网
get 从服务器端获取资源
put 提交资源
post 更新资源
delete 删除资源
connect 建立tunnel隧道
100 请求已收到,正等待后续资源
200 ok 成功
206 partial content 部分资源
301 永久重定向
400 bad request 客户端请求语法错误
https是http加上ssl的应用层协议。在http的基础上增加了安全性和可靠性。
端口的不同:http默认是80端口, https默认是443端口
安全性:http是明文传输,https是密文传输。
认证:http没有认证,https在建立TCP连接前会进行ssl层的握手,在这个过程中需要认证。
成本上:https的证书需要成本,同时加密和解密时对CPU和内存开销增加。
https的原理:
https通信时,首先建立ssl层的连接,客户端将ssl版本号和加密组件发到客户端,客户端收到后对ssl版本号和加密组件进行匹配,同时将CA证书及**发送到客户端。客户端对证书进行验证,验证通过后使用非对称加密对数据通信时的**进行协商。协商后得到一致的获得一致的对称加***。然后使用对称加密算法进行TCP连接,后续的过程跟http的过程一致。三次握手,数据交换,四次挥手,通信结束。
发表于 2020-03-19 10:05:25
上一篇: java1.5新特性
下一篇: Android性能优化-Render篇