网易2021校招笔试-C++开发工程师(正式第一批)
一、树上摘樱桃(易)
1.1 题目描述
二叉树上的叶子节点定义为“樱桃”。现在需要找出二叉树上有多少个有两颗“樱桃”的结点。
输入描述:
第一行两个正整数m, n,空格分开,分别代表总共有树上有多少个节点,树上有多少条边,2<=m<=100, 1<=n<=100
下面有n行,每行为3个部分,用空格分割,第一个数字为某非叶子节点的id, 第二个为该边为left还是right,第三个为子节点的id
注意:节点id彼此不会重复,id 1为根节点
输入:
10 9
1 left 2
1 right 3
2 left 4
2 right 5
3 right 6
6 left 7
6 right 8
8 left 9
8 right 10
结果:2
1.2 题目分析
遍历所有节点,计算一共有多少个节点(有两个孩子且这两个孩子为叶子结点)
1、如何存储二叉树;(vector<pair<string, int> > edge[N + 1]); string:左子树or右子树
1.3 代码实现
#include<iostream>
#include<vector>
#include<map>
#include<string>
using namespace std;
const int maxn = 102;
vector<pair<string, int> > edge[maxn];
int m, n; // m代表节点数 n代表边数
bool check(int x){ // 检查节点x是否为叶子节点
if(edge[x].size() == 0)
return true;
return false;
}
int main()
{
cin >> m >> n;
string s;
int id1, id2;
for(int i = 0; i < n; i++){
cin >> id1 >> s >> id2;
edge[id1].push_back(make_pair(s, id2));
}
int res = 0; // 记录最终结果
for(int i = 1; i <= m; i++){
if(edge[i].size() == 2 && check(edge[i][0].second) && check(edge[i][1].second))
res++;
}
cout << res << '\n';
return 0;
}
二、特征排列组合(递归)
2.1 题目描述
输入描述:
输入n,表示总共有多少组不同的特征,1<=n<=100
下面有n行,每一行特征为该组的所有取值,用空格区分, 每一行的特征值数量不大于100,每个特征值为英文或者数字组合成的字符串。
输出描述:
输出为排列组合后的所有组合值,每个组合值为一行,不同组的特征值之间用"-"连接,显示顺序保持的原特征的显示顺序关系(参照例子)
输入:
3
man woman
coder gamer painter
phd
输出:
man-coder-phd
woman-coder-phd
man-gamer-phd
woman-gamer-phd
man-painter-phd
woman-painter-phd
2.2 题目分析
1、如何输入?见代码
2、不可能为每一种特征都写一个for循环,因此,想到递归。
3、从输出顺序可以看出此为倒序DFS。
2.3 代码实现
#include<iostream>
#include<vector>
#include<string>
using namespace std;
const int maxn = 102;
int n;
vector<string> v[maxn];
void dfs1(int x, string path){ // 正序DFS
if(x > n){
cout << path << '\n';
return ;
}
for(int i = 0; i < v[x].size(); i++){
if(x != 1)
dfs1(x + 1, path + '-' + v[x][i]);
else
dfs1(x + 1, path + v[x][i]);
}
}
void dfs2(int x, string path){ // 倒序DFS
if(x == 0){
cout << path << '\n';
return ;
}
for(int i = 0; i < v[x].size(); i++){
if(x != n)
dfs2(x - 1, v[x][i] + '-' + path);
else
dfs2(x - 1, v[x][i] + path);
}
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++){
string s;
while(cin >> s){
v[i].push_back(s);
if(cin.get() == '\n')
break;
}
}
dfs1(1, "");
dfs2(n, "");
return 0;
}
三、特殊的编辑距离(动态规划DP)
3.1 题目描述
在自然语言处理的过程中,经常需要判断一个字符串和另外一个字符串之间的一个相似程度,其中常见的一个指标就是编辑距离,即一个字符串最少经过多少次“增删改”某个字符,可以变为另一个字符串。如“abc”与“ac”的编辑距离为1,是因为在a和c中间“增加”一个b即可。如“abcd”与“axc”的编辑距离为2,是因为把“abcd”的b修改为x,然后再删除d即可,共2次操作。
但是在某种场景中,编辑距离定义为词粒度的。比如句子A “I am a coder”与句子B “hello , I am a singer”之间,对于句子A可以通过添加"hello"和符号",“, 并替换"coder"为"singer”,共3个操作得到句子B。所以可得其基本的编辑距离为3。
在本题中,特别地,对于部分词,比如标点符号“, ”、"hello"对于句子语义的影响并不重要,这部分称之为停用词,这部分可以在匹配的过程中被跳过。比如对于句子A “I am a coder”与句子B “hello , I am a singer”,如果加入了停用词的影响,那编辑距离从3降到1。
所以目标是可以有选择性地跳过停用词的情况下,问最小的编辑距离是多少。
输入描述:
共3行
第一行为停用词列表,用空格区分
第二行为句子A,所有词可以用空格区分,词数不超过10000
第三行为句子B,所有词可以用空格区分,词数不超过10000
输入:
hello , ?
I am a boy
are you a girl ?
输出:3
3.2 题目分析
1、三种操作:增加、删除、修改;
2、既然停用词对结果没影响,那么直接将两句话中的停用词全部删除;
3、动态规划
3.3 代码实现
#include<iostream>
#include<string>
#include<vector>
#include<unordered_set>
#include<algorithm>
using namespace std;
unordered_set<string> stop;
vector<string> A;
vector<string> B;
int main()
{
string s;
while(cin >> s){
stop.insert(s);
if(cin.get() == '\n')
break;
}
while(cin >> s){
if(!stop.count(s))
A.push_back(s);
if(cin.get() == '\n')
break;
}
while(cin >> s){
if(!stop.count(s))
B.push_back(s);
if(cin.get() == '\n')
break;
}
int lengthA = A.size();
int lengthB = B.size();
vector<vector<int> > dp(lengthA + 1, vector<int>(lengthB + 1, 0));
// dp[i][j]表示表示A串从第0个字符开始到第i个字符和B串从第0个字符开始到第j个字符,这两个字串的编辑距离, A和B是从下标为0处开始的
for(int i = 0; i <= lengthA; i++)
dp[i][0] = i;
for(int j = 0; j <= lengthB; j++)
dp[0][j] = j;
for(int i = 1; i <= lengthA; i++)
for(int j = 1; j <= lengthB; j++){
// 此处为何是A[i-1]和B[j-1]
// 因为A串的第i个字符即为A[i-1]
if(A[i - 1] == B[j - 1])
dp[i][j] = dp[i - 1][j - 1];
else
dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1;
// dp[i - 1][j - 1] + 1 替换(修改)
// dp[i - 1][j] + 1 删掉字符串a最后一个字符a[i]
// dp[i][j - 1] + 1 给字符串添加b最后一个字符
}
cout << dp[lengthA][lengthB] << '\n';
return 0;
}
四、组合幸运数字(难)
4.1 题目描述
小易的幸运数字是7,现有一个整数数组 nums,请你找出并返回能被七整除的子集合的最大和,如果找不到则返回-1。
输入描述:
一个正整数数组列表nums,用空格区分,1<=length(nums)<=100000,sum(nums) <= 1000000000
7 3 1 4
输出:14 (3 4 7)
6 5
输出:-1 (没有)
4.2 题目分析
1、学会思路;
2、学会for(int v : vector< int>(dp))。
4.3 代码实现
#include<iostream>
#include<vector>
using namespace std;
const int maxn = 100002;
vector<int> nums;
int main()
{
int cur;
while(cin >> cur)
nums.push_back(cur);
vector<int> dp(7, 0); // dp[i] 余数为i的最大和
for(int num : nums)
for(int v : vector<int>(dp)){
dp[(num + v) % 7] = max(dp[(num + v) % 7], num + v);
}
if(dp[0] == 0)
cout << "-1" << '\n';
else
cout << dp[0] << '\n';
return 0;
}