首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >新鲜出炉的阿里、虾皮和网易笔试题解

新鲜出炉的阿里、虾皮和网易笔试题解

作者头像
ACM算法日常
发布2021-09-28 16:27:25
发布2021-09-28 16:27:25
1.3K00
代码可运行
举报
文章被收录于专栏:ACM算法日常ACM算法日常
运行总次数:0
代码可运行

9.18 日网易笔试在牛客上闹出大瓜,笔试还没结束的时候候选人在牛客评论区公然交流代码,事后牛客工作人员获取了后台记录,并上报给了网易,直接取消了 50 余人的校招资格

虽说今年秋招笔试题有点恶心人,但是考试还是得看真本事,天网恢恢,疏而不漏,动了歪心思,就有可能陷入万劫不复之地

9.22 阿里笔试,1h

题目一

给定

n

个车,和

k

个车库,给定入库顺序和出库顺序,计算多少个车抢占了次序

例如数据

代码语言:javascript
代码运行次数:0
运行
复制
5 2
1 2 3 4 5
3 2 5 4 1

其中

3,\ 5

抢占了次序,输出

2

再例如数据

代码语言:javascript
代码运行次数:0
运行
复制
5 3
1 2 3 4 5
3 2 1 4 5

没有车抢占顺序,因此输出

0

数据规定

1\leq k\leq n\leq 10^5

题解

  • set 维护一个滑动窗口,维护车库里有哪些车
  • 遍历出库顺序,若当前车辆不在窗口内,说明其抢占,否则其未抢占
代码语言:javascript
代码运行次数:0
运行
复制
// cpp
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 7;
int t, n, k;
int in[N], out[N], vis[N];
int solve(int n, int k) {
    memset(vis, 0, sizeof(vis));
    set<int> st;
    for (int i = 1; i <= k; ++i) st.insert(in[i]);
    int j = k + 1, i = 1;
    int ans = 0;
    for (int i = 1; i <= n; ++i) {
        if (st.count(out[i]) == 0) ans++, vis[out[i]] = 1;
        else st.erase(st.find(out[i]));
        while (j <= n && st.size() < k) {
            if (!vis[in[j]]) st.insert(in[j]);
            ++j;
        }
    }
    return ans;
}
int main()
{
    scanf("%d", &t);
    while (t--) {
        scanf("%d %d", &n, &k);
        for (int i = 1; i <= n; ++i) scanf("%d", &in[i]);
        for (int i = 1; i <= n; ++i) scanf("%d", &out[i]);
        cout << solve(n, k) << endl;
    }
    return 0;
}
/*
2
5 2
1 2 3 4 5
3 2 5 1 4
5 3
1 2 3 4 5
3 2 1 4 5
*/

题目二

原来有一个数组,对于其中每一个元素

X

,现在都变成了第

X

个素数

现在给定一个字符串

s

,表示变换后的数组中数字拼接的情况,需要你还原出原来的数组,如果有多个答案,输出数组长度最长的那一个

在这个问题中,我们认为

1

是第

0

个素数,同时保证变换后的最大素数不超过

10000

例如 1719113 可以还原成 [0 4 8 0 0 2]

再例如 103 可以还原成 [27]

数据规定,

1\leq |s|\leq 500000

题解

  • 先预处理出
10000

内的所有素数,并建立反向索引

  • 定义
dp[i]

表示到

i

位置位置,数组的最大长度

  • 考虑转移,枚举位置
j

,使得

s[j+1:i]

是素数,注意前导零的情况

  • 考虑到素数最大不超过
10000

,所以

j

i - 3

开始枚举即可

  • 为了还原答案,需要记录前继状态,开 pre, val 数组维护即可
  • 时间复杂度
O(10\cdot |s|)
代码语言:javascript
代码运行次数:0
运行
复制
// cpp
#include <bits/stdc++.h>
using namespace std;

const int N = 1e4 + 1;
int vis[N], prime[N], cnt = 0;
void init() {
    vis[1] = 0;
    prime[++cnt] = 1;
    for (int i = 2; i < N; ++i) {
        if (!vis[i]) prime[++cnt] = i;
        for (int j = 1; j * i < N; ++j) {
            vis[j * i] = 1;
        }
    }
}

const int MAXN = 5e5 + 7;
char str[MAXN];
int dp[MAXN], pre[MAXN], val[MAXN], mp[N];
int main()
{
    init();
    for (int i = 1; i <= cnt; ++i) mp[prime[i]] = i;
    scanf("%s", str + 1);
    int L = strlen(str + 1);
    for (int i = 1; i <= L; ++i) {
        for (int j = max(0, i - 4 + 1); j < i; ++j) {
            int temp = 0;
            if (str[j + 1] == '0') temp = 0; // 有前导零
            else for (int k = j + 1; k <= i; ++k) temp *= 10, temp += str[k] - '0';
            if (mp[temp] > 0 && (dp[j] + (mp[temp] > 0)) > dp[i]) {
                pre[i] = j, val[i] = mp[temp] - 1; // 记录前继
                dp[i] = dp[j] + (mp[temp] > 0);
            }

        }
    }
    cout << dp[L] << endl;
    int cur = L;
    vector<int> ans;
    while (cur) {
        ans.push_back(val[cur]);
        cur = pre[cur];
    }
    for (int i = dp[L] - 1; i >= 0; --i) printf("%d%c", ans[i], " \n"[i == 0]);
    return 0;
}

9.22 虾皮笔试,20 min

为了面快手三面,飞速的做完了所有的题

题目一

给定长为

n

的字符串数组

C

,给定目标字符串

I

,找出

C

里面与

I

最接近的字符串

题解

编辑距离走一波就可以

代码语言:javascript
代码运行次数:0
运行
复制
// cpp
class Solution {
public:
    int solve(string command, string input) {
        int m = command.size();
        int n = input.size();
        vector<vector<int>> dp(m + 7, vector<int>(n + 7));
        for (int i = 0; i <= n; ++i) dp[0][i] = i;
        for (int j = 0; j <= m; ++j) dp[j][0] = j;
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1;
                if (command[i - 1] == input[j - 1]) dp[i][j] = min(dp[i - 1][j - 1], dp[i][j]);
                else dp[i][j] = min(dp[i - 1][j - 1] + 1, dp[i][j]);
            }
        }
        return dp[m - 1][n - 1];
    }
    string didYouMean(vector<string>& commands, string input) {
        int ans = 0;
        string val;
        for (auto &i: commands) {
            int temp = solve(i, input);
            if (temp < ans) ans = temp, val = i;
        }
        return val;
    }
};

题目二

给定

n

个物品,每一个物品有一个大小

a_{i}

,给定

m

个袋子,求出最小的袋子容量,使得按顺序可以把所有物品都装入

题解

二分答案

k

,遍历 check

代码语言:javascript
代码运行次数:0
运行
复制
// cpp
class Solution {
public:
    bool check(int n, int m, int k, vector<int> &weights) {
        int cnt = 0, temp = 0;
        for (int i = 0; i < n; ++i) {
            if (temp + weights[i] <= k) temp += weights[i];
            else cnt++, temp = weights[i]; 
        }
        if (temp) cnt++;
        return cnt <= m;
    }
    long long Solve(int n, int m, vector<int>& weights) {
        int L = 0, R = 0;
        for (auto &i: weights) R += i, L = min(L, i); 
        int ans = 0;
        while (L <= R) {
            int mid = L + R >> 1;
            if (check(n, m, mid, weights)) ans = mid, R = mid - 1;
            else L = mid + 1;
        }
        return ans;
    }
};

题目三

给定数组

a

,每一个元素

a_{i}

表示他的权,需要把

n

个元素两两合并,使得合并后的所消耗权最小

题解

哈夫曼树

代码语言:javascript
代码运行次数:0
运行
复制
class Solution {
public:
    int minEffort(vector<int>& cases) {
        priority_queue<int, vector<int>, greater<int>> pq;
        for (auto &i: cases) pq.push(i);
        int ans = 0;
        while (pq.size() > 1) {
            int x = pq.top(); pq.pop();
            int y = pq.top(); pq.pop();
            ans += x + y;
            pq.push(x + y);
        }
        return ans;
    }
};

9.18 网易笔试,2h

笔者在牛客网的编辑器写的,忘记记录代码了,在此说一下思路

题目一

给定正整数

n

,计算

n

能够整除自身的位数

例如 120 每一位都可以整除自身,所以输出

3

题解

把每一位取出来做模运算即可,用计数器维护答案

题目二

给定一个循环的字母键盘,记录了

26

个大写字母,现在有三个操作

  • 点击键,消耗一个体力
  • 移动键(左右两个方向),消耗一个体力,由于是循环的,所以从
A

Z

或者从

Z

A

消耗

1

体力

  • 魔法键,可以不花费体力瞬移到你想要的字母上去,但是一旦按下了魔法键,接下来必须得按
k - 1

次魔法键,也就是说,魔法键要么不按,要么连续按

k

给定一个只包含大写字母的字符串

s

,计算能够打出这个字符串的最小体力数,保证有解

题解

我们发现,点击键所消耗的体力是不可缺少的,即为

|s|

魔法键必须按

k

次,也就是维护了一个长度为

k

的窗口,在这

k

个窗口中,不消耗移动体力

我们用

pre[i]

记录到

s

的第

i

个位置,所需要的移动体力的前缀和,处理移动体力时应该选最少的移动体力,也就是说要记录一下向左移动还是向右移动

然后滑动长度为

k

的魔法窗口,对于当前情况,如果窗口起始于

i

,结束于

i + k - 1

,那么移动所消耗的体力即为

pre[i] + pre[n] - pre[i + k - 1]

,维护这个最大值即可

最后的答案加上

|s|

,时间复杂度为

O(|s|)

题目三

给定一个非负整数

n

,如果他可以由多个正整数

x_1,\ x_2,\ ...,\ x_n

,以

2^{x_1} + 2^{x_2} + .. + 2^{x_n}

或者

2^{x_1} - 2^{x_2} - .. - 2^{x_n}

的方式得到,那么选一个二进制中

1

最少的方案,如果无法拼凑,那就输出

-1

例如

30 = 32 - 2 = (100000)_2 - (10)_2

,其中一共出现

2

1

再比如

33 = 32 + 1 = (100000)_2 + (1)_2

,其中一共出现

2

1

题解

首先考虑构不成的情况,即

n = 0

对于构成,需要考虑两种情况

  • 本身二进制数中
1

的个数

  • 最近的
2

的幂减去本身,这个过程出现的

1

的个数

两个情况取一个大的即可

题目四

给定一个

n\times n

的迷宫,由 . * # 三种路标组成

  • # 不可走,但是可以花费 a 能量强制移动
  • * 可走,也可以花费 b 直接移动到另一个 *
  • . 可走,不花费能量

现在你位于左上角,能够往上下左右四个方向移动,并且希望以最小的能量消耗走到右下角,请输出最小的能量消耗

题解

由于移动方向是上下左右,所以直接排除 dp,考虑搜索算法

经典的单源最短路,可以使用优先队列配合 bfs,这种算法叫做 uniform cost seasrch,是人工智能里的搜索算法,我实现了这个算法,并且 ac

更高效的算法是建图后跑 dijkstra,不过比较麻烦,对时间复杂度有要求的话得上这个算法

题目五

说一说装饰器模式和适配器模式的区别

题解

前者是对一个类的封装,后者是对两个类的连接

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ACM算法日常 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 9.22 阿里笔试,1h
    • 题目一
    • 题解
    • 题目二
    • 题解
  • 9.22 虾皮笔试,20 min
    • 题目一
    • 题解
    • 题目二
    • 题解
    • 题目三
    • 题解
  • 9.18 网易笔试,2h
    • 题目一
    • 题解
    • 题目二
    • 题解
    • 题目三
    • 题解
    • 题目四
    • 题解
    • 题目五
    • 题解
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档