腾讯2020校园招聘-后台&综合-第一次笔试 题解
对数据结构和算法感兴趣的可以关注一下https://github.com/MCQ1999/Datastructure_Algorithm_Solutions,分享算法题的解题思路和代码~
1.压缩算法(栈模拟)
题意
链接:https://www.nowcoder.com/questionTerminal/c27561e5b7e0441493adb9a54071888d
来源:牛客网
小Q想要给他的朋友发送一个神秘字符串,但是他发现字符串的过于长了,于是小Q发明了一种压缩算法对字符串中重复的部分进行了压缩,对于字符串中连续的m个相同字符串S将会压缩为m|S,例如字符串ABCABCABC将会被压缩为[3|ABC],现在小Q的同学收到了小Q发送过来的字符串,你能帮助他进行解压缩么?
输入描述:
输入第一行包含一个字符串s,代表压缩后的字符串。
S的长度<=1000;
S仅包含大写字母、[、]、|;
解压后的字符串长度不超过100000;
压缩递归层数不超过10层;
输出描述:
输出一个字符串,代表解压后的字符串。
示例1
输入
HG[3|B[2|CA]]F
输出
HGBCACABCACABCACAF
说明
HG[3|B[2|CA]]F−>HG[3|BCACA]F−>HGBCACABCACABCACAF
思路
栈模拟。
代码
#include<bits/stdc++.h>
using namespace std;
int main(){
stack<char> st;
string s;
cin>>s;
int n=s.length();
for(int i=0;i<n;i++){
if(s[i]!=']'){
st.push(s[i]);
}else {
string tmp="";
while(st.top()!='|'){
tmp+=st.top();
st.pop();
}
reverse(tmp.begin(),tmp.end());
string num="";
st.pop();
while(st.top()!='['){
num+=st.top();
st.pop();
}
st.pop();
reverse(num.begin(),num.end());
int x=stoi(num);
string t="";
while(x--){
for(char c:tmp){
st.push(c);
}
}
}
}
string ans;
while(!st.empty()){
ans+=st.top();
st.pop();
}
reverse(ans.begin(),ans.end());
cout<<ans<<endl;
return 0;
}
2.逛街 (单调栈)
题意
链接:https://www.nowcoder.com/questionTerminal/35fac8d69f314e958a150c141894ef6a
来源:牛客网
小Q在周末的时候和他的小伙伴来到大城市逛街,一条步行街上有很多高楼,共有n座高楼排成一行。
小Q从第一栋一直走到了最后一栋,小Q从来都没有见到这么多的楼,所以他想知道他在每栋楼的位置处能看到多少栋楼呢?(当前面的楼的高度大于等于后面的楼时,后面的楼将被挡住)
输入描述:
输入第一行将包含一个数字n,代表楼的栋数,接下来的一行将包含n个数字wi(1<=i<=n),代表每一栋楼的高度。
1<=n<=100000;
1<=wi<=100000;
输出描述:
输出一行,包含空格分割的n个数字vi,分别代表小Q在第i栋楼时能看到的楼的数量。
示例1
输入
6
5 3 8 3 2 5
输出
3 3 5 4 4 4
说明
当小Q处于位置3时,他可以向前看到位置2,1处的楼,向后看到位置4,6处的楼,加上第3栋楼,共可看到5栋楼。当小Q处于位置4时,他可以向前看到位置3处的楼,向后看到位置5,6处的楼,加上第4栋楼,共可看到4栋楼。
思路
单调栈。
维护从栈底到栈顶递减的栈,这么做的意义是正着遍历可以求出每个点往左边能看到的个数,倒着遍历求出每个点往右能看到的个数。比如正着遍历,因为栈里面元素是单减的,那么每个点往左看,栈里面都是大于这个点的,所以每次栈的大小就是对应的个数。最后再加上1就是答案了。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N],b[N];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
stack<int> st;
for(int i=1;i<=n;i++){
b[i]+=st.size();
while(!st.empty()&&a[i]>=st.top()){
st.pop();
}
st.push(a[i]);
}
while(!st.empty()) st.pop();
for(int i=n;i>=1;i--){
b[i]+=st.size();
while(!st.empty()&&a[i]>=st.top()){
st.pop();
}
st.push(a[i]);
}
for(int i=1;i<=n;i++){
printf("%d ",b[i]+1);
}
puts("");
return 0;
}
3.逆序对
题意
链接:https://www.nowcoder.com/questionTerminal/8fe007e54fc04b5e82089aaa71ba3553
来源:牛客网
作为程序员的小Q,他的数列和其他人的不太一样,他有2n2^n2n个数。
老板问了小Q一共 m次,每次给出一个整数qi(1<=i<=m)q_i (1 <= i <= m)qi(1<=i<=m), 要求小Q把这些数每2qi2^{q_i}2qi分为一组,然后把每组进行翻转,小Q想知道每次操作后整个序列中的逆序对个数是多少呢?
例如:
对于序列1 3 4 2,逆序对有(4, 2),(3, 2),总数量为2。
翻转之后为2 4 3 1,逆序对有(2, 1),(4, 3), (4, 1), (3, 1),总数量为4。
输入描述:
第一行一个数n(0≤n≤20)n(0 \leq n \leq 20)n(0≤n≤20)
第二行2n2^n2n个数,表示初始的序列(1≤初始序列≤1091 \leq 初始序列 \leq 10^91≤初始序列≤109)
第三行一个数m(1≤m≤106)m(1 \leq m \leq 10^6)m(1≤m≤106)
第四行m个数表示qi(0≤qi≤n)q_i(0 \leq q_i \leq n)qi(0≤qi≤n)
输出描述:
m行每行一个数表示答案。
示例1
输入
2
2 1 4 3
4
1 2 0 2
输出
0
6
6
0
说明
初始序列2 1 4 3
2q1=22^{q_1} = 22q1=2 ->
第一次:1 2 3 4 -> 逆序对数为0
2q2=42^{q_2} = 42q2=4 ->
第二次:4 3 2 1 -> 逆序对数为6
2q3=12^{q_3} = 12q3=1 ->
第三次:4 3 2 1 -> 逆序对数为6
2q4=42^{q_4} = 42q4=4 ->
第四次:1 2 3 4 -> 逆序对数为0
思路
咕咕咕
4.假期(动态规划)
题意
链接:https://www.nowcoder.com/questionTerminal/7cd9a140387e455a972e8fea0e74be2c
来源:牛客网
由于业绩优秀,公司给小Q放了 n 天的假,身为工作狂的小Q打算在在假期中工作、锻炼或者休息。他有个奇怪的习惯:不会连续两天工作或锻炼。只有当公司营业时,小Q才能去工作,只有当健身房营业时,小Q才能去健身,小Q一天只能干一件事。给出假期中公司,健身房的营业情况,求小Q最少需要休息几天。
输入描述:
第一行一个整数 n(1≤n≤100000)n(1\leq n\leq 100000)n(1≤n≤100000) 表示放假天数
第二行 n 个数 每个数为0或1,第 i 个数表示公司在第 i 天是否营业
第三行 n 个数 每个数为0或1,第 i 个数表示健身房在第 i 天是否营业
(1为营业 0为不营业)
输出描述:
一个整数,表示小Q休息的最少天数
示例1
输入
4
1 1 0 0
0 1 1 0
输出
2
说明
小Q可以在第一天工作,第二天或第三天健身,小Q最少休息2天
思路
dp(i,0)表示在到第i天工作所能休息的最少天数,dp(i,1)表示在到第i天健身所能休息的最少天数,dp(i,2)表示在到第i天休息所能休息的最少天数。
那么
如果第i天能工作,肯定是从第i-1天不工作转移过来
如果第i天能健身,肯定是从第i-1天不健身转移过来
第i天休息,则是从第i-1天三种状态转移过来,因为没有要求不能连续休息两天。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N],b[N],dp[N][3];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
}
memset(dp,0x3f3f3f3f,sizeof(dp));
if(a[1]){
dp[1][0]=0;
}
if(b[1]){
dp[1][1]=0;
}
dp[1][2]=1;
for(int i=2;i<=n;i++){
if(a[i]){
dp[i][0]=min(dp[i-1][1],dp[i-1][2]);
}
if(b[i]){
dp[i][1]=min(dp[i-1][0],dp[i-1][2]);
}
dp[i][2]=min(dp[i-1][0],min(dp[i-1][1],dp[i-1][2]))+1;
}
printf("%d\n",min(dp[n][0],min(dp[n][1],dp[n][2])));
return 0;
}
5.视野争夺 (贪心)
题意
链接:https://www.nowcoder.com/questionTerminal/61e1e66e39f348cdb6495de91ac36a41
来源:牛客网
小Q在进行一场竞技游戏,这场游戏的胜负关键就在于能否能争夺一条长度为L的河道,即可以看作是[0,L]的一条数轴。
这款竞技游戏当中有n个可以提供视野的道具−真视守卫,第i个真视守卫能够覆盖区间[xi,yi]。现在小Q想知道至少用几个真视守卫就可以覆盖整段河道。
输入描述:
输入包括n+1行。
第一行包括两个正整数n和L(1<=n<=105,1<=L<=109)
接下来的n行,每行两个正整数xi,yi(0<=xi<=yi<=109),表示第i个真视守卫覆盖的区间。
输出描述:
一个整数,表示最少需要的真视守卫数量, 如果无解, 输出-1。
示例1
输入
4 6
3 6
2 4
0 2
4 7
输出
3
思路
按左端点从小到大,右端点从大到小排序,每次看一个区间能里的点往右最大能延伸到哪(mx),下次更新上限(up)的时候就用这个mx。这个过程和leetcode跳跃游戏那题很像。
代码
#include<bits/stdc++.h>
using namespace std;
struct node{
int l,r;
bool operator<(node b){
if(l==b.l){
return r>b.r;
}
return l<b.l;
}
}g[100005];
int main(){
int n,L;
scanf("%d%d",&n,&L);
for(int i=0;i<n;i++){
scanf("%d%d",&g[i].l,&g[i].r);
}
sort(g,g+n);
int up=g[0].r,mx=g[0].r,cnt=1;
if(g[0].l>0) {
printf("-1\n");
return 0;
}
for(int i=1;i<n;i++){
if(g[i].l<=up){
mx=max(g[i].r,mx);
if(mx>=L){
printf("%d\n",cnt+1);
return 0;
}
}else {
cnt++;
up=max(up,mx);
i--;
}
}
if(mx<L){
printf("-1\n");
return 0;
}
printf("%d\n",cnt);
return 0;
}
总结
腾讯的笔试题做起来还是有点难度的,考察点也比较全(贪心、dp、数据结构),题目也很有价值,值得反复做。