🍭 大家好这里是KK爱Coding ,一枚热爱算法的程序员
✨ 本系列打算持续跟新携程近期的春秋招笔试题汇总~
💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导
👏 感谢大家的订阅➕ 和 喜欢💗
📧 KK这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 KK领取,会在飞书进行同步的跟新,5月1日之前限时免费提供ing~
文章目录
🛎 01.字符串修改
题目描述
LYA 正在学习英语单词,她想要在一个由小写字母组成的字符串中添加一些字符,使得修改后的字符串包含尽可能多的单词 “you”。每次添加操作只能在字符串的任意位置添加一个字符 ‘0’。LYA 想知道最少需要添加几次字符,才能使修改后的字符串包含最多的单词 “you”。
输入格式
输入一个仅由小写字母组成的字符串 s s s,字符串的长度不超过 2 × 1 0 5 2 \times 10^5 2×105。
输出格式
输出一个整数,表示最少需要添加的字符数量。
样例输入
yuyouy
样例输出
1
数据范围
- 1 ≤ ∣ s ∣ ≤ 2 × 1 0 5 1 \leq |s| \leq 2 \times 10^5 1≤∣s∣≤2×105
- 字符串 s s s 仅由小写字母组成
题解
这道题可以通过贪心的思想来解决。我们可以遍历字符串,统计相邻的字符 ‘y’ 和 ‘u’ 的个数,即统计子串 “yu” 的出现次数。最少需要添加的字符数量就等于子串 “yu” 的出现次数。
具体步骤如下:
- 初始化结果变量 r e s res res 为 0 0 0,表示最少需要添加的字符数量。
- 遍历字符串
s
s
s,对于每个位置
i
i
i:
- 如果当前字符 s [ i ] s[i] s[i] 为 ‘u’,且前一个字符 s [ i − 1 ] s[i-1] s[i−1] 为 ‘y’,则将 r e s res res 的值加 1 1 1。
- 遍历结束后,输出 r e s res res 的值作为最少需要添加的字符数量。
时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1),其中 n n n 为字符串的长度。
参考代码
- Python
s = input()
n = len(s)
res = 0
for i in range(1, n):
if s[i] == 'u' and s[i - 1] == 'y':
res += 1
print(res)
- Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.next();
int n = s.length();
int res = 0;
for (int i = 1; i < n; i++) {
if (s.charAt(i) == 'u' && s.charAt(i - 1) == 'y') {
res++;
}
}
System.out.println(res);
}
}
- Cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
cin >> s;
int n = s.length();
int res = 0;
for (int i = 1; i < n; i++) {
if (s[i] == 'u' && s[i - 1] == 'y') {
res++;
}
}
cout << res << endl;
return 0;
}
🛍 02.K小姐的礼物分配问题
问题描述
K小姐准备给她的三个好朋友A先生、B先生和C先生送礼物。她准备了三份礼物,分别装在三个盒子里,盒子的大小分别用正整数 a i a_i ai, b i b_i bi, c i c_i ci 表示。
为了公平起见,K小姐希望尽可能多的盒子满足条件: a i + b i = c i a_i+b_i=c_i ai+bi=ci,即某个盒子的大小恰好等于另外两个盒子大小的和。现在K小姐想知道,在不改变 a a a 和 b b b 盒子大小的情况下,最多有多少个盒子能满足上述条件?
输入格式
第一行包含一个正整数 n n n,表示每份礼物盒子的数量。
第二行包含 n n n 个正整数 a 1 , a 2 , ⋯ , a n a_1, a_2, \cdots, a_n a1,a2,⋯,an,表示第一份礼物中每个盒子的大小。
第三行包含 n n n 个正整数 b 1 , b 2 , ⋯ , b n b_1, b_2, \cdots, b_n b1,b2,⋯,bn,表示第二份礼物中每个盒子的大小。
第四行包含 n n n 个正整数 c 1 , c 2 , ⋯ , c n c_1, c_2, \cdots, c_n c1,c2,⋯,cn,表示第三份礼物中每个盒子的大小。
输出格式
输出一个整数,表示最多有多少个盒子满足条件 a i + b i = c i a_i+b_i=c_i ai+bi=ci。
样例输入
4
1 2 3 4
2 3 4 5
4 5 6 7
样例输出
2
数据范围
- 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105
- 1 ≤ a i , b i , c i ≤ 2 × 1 0 9 1 \leq a_i, b_i, c_i \leq 2 \times 10^9 1≤ai,bi,ci≤2×109
题解
可以用哈希表来解决这个问题。具体步骤如下:
-
用一个哈希表 d d d 统计数组 a a a 和 b b b 中每一对元素的和出现的次数。
-
用另一个哈希表 c c c 统计数组 c c c 中每个元素出现的次数。
-
遍历哈希表 d d d,对于每个键值对 ( k , v ) (k, v) (k,v),如果 k k k 也在哈希表 c c c 中出现,则说明有 min ( v , c [ k ] ) \min(v, c[k]) min(v,c[k]) 个盒子满足条件 a i + b i = c i a_i+b_i=c_i ai+bi=ci,将这个值累加到答案中。
时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)。
参考代码
- Python
import sys
from collections import Counter
def solve():
n = int(sys.stdin.readline())
a = list(map(int, sys.stdin.readline().split()))
b = list(map(int, sys.stdin.readline().split()))
c_cnt = Counter(map(int, sys.stdin.readline().split()))
ab_sum_cnt = Counter()
for x, y in zip(a, b):
ab_sum_cnt[x + y] += 1
res = 0
for s, cnt in ab_sum_cnt.items():
res += min(cnt, c_cnt[s])
print(res)
if __name__ == '__main__':
solve()
- Java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
int[] a = Arrays.stream(br.readLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int[] b = Arrays.stream(br.readLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int[] c = Arrays.stream(br.readLine().split(" ")).mapToInt(Integer::parseInt).toArray();
Map<Integer, Integer> abSumCnt = new HashMap<>();
for (int i = 0; i < n; i++) {
int sum = a[i] + b[i];
abSumCnt.put(sum, abSumCnt.getOrDefault(sum, 0) + 1);
}
Map<Integer, Integer> cCnt = new HashMap<>();
for (int x : c) {
cCnt.put(x, cCnt.getOrDefault(x, 0) + 1);
}
int res = 0;
for (Map.Entry<Integer, Integer> entry : abSumCnt.entrySet()) {
int sum = entry.getKey(), cnt = entry.getValue();
if (cCnt.containsKey(sum)) {
res += Math.min(cnt, cCnt.get(sum));
}
}
System.out.println(res);
}
}
- Cpp
#include <iostream>
#include <unordered_map>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
int a[n], b[n], c[n];
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) cin >> b[i];
for (int i = 0; i < n; i++) cin >> c[i];
unordered_map<int, int> abSumCnt;
for (int i = 0; i < n; i++) {
abSumCnt[a[i] + b[i]]++;
}
unordered_map<int, int> cCnt;
for (int i = 0; i < n; i++) {
cCnt[c[i]]++;
}
int res = 0;
for (auto& p : abSumCnt) {
int sum = p.first, cnt = p.second;
if (cCnt.count(sum)) {
res += min(cnt, cCnt[sum]);
}
}
cout << res << endl;
return 0;
}
🪞 03.老K的生日宴会
老K是一位非常有名的数学家,他的朋友遍布全球。一年一度的生日宴会上,来自世界各地的朋友们都会带来一份礼物,其中有些礼物的价值对数学家老K来说更有研究价值。
在宴会上,大家玩起了一个有趣的游戏:每次可以选择相邻的两件礼物,如果这两件礼物都是质数,就可以将它们合并成一件新的礼物,价值等于两件礼物的价值之和。合并后原来的两件礼物就从礼物列表中删除了,由新的礼物代替。
老K希望通过合并,使得最终剩下的礼物数量尽可能少。作为老K的好朋友,你能帮助他计算出最少能剩下多少件礼物吗?
输入格式
第一行包含一个正整数 n n n,表示初始时的礼物数量。
第二行包含 n n n 个正整数,表示从左到右每件礼物的价值。
输出格式
输出一个整数,表示合并后最少的礼物数量。
样例输入
5
1 3 2 5 4
样例输出
3
数据范围
1
≤
n
≤
1
0
5
1 \le n \le 10^5
1≤n≤105
1
≤
a
i
≤
1
0
6
1 \le a_i \le 10^6
1≤ai≤106
题解
本题的关键是要考虑合并的顺序。
首先,除了 2 2 2 以外的质数都是奇数,奇数与奇数相加一定是偶数,不可能再与其他数合并。而 2 2 2 与一个质数相加,有可能还是质数,有可能继续合并。所以我们要优先考虑 2 2 2 的合并。
遍历礼物列表,如果遇到 2 2 2,就看看左右两边的数是否为质数,如果是质数并且与 2 2 2 相加还是质数,就进行合并。
除了 2 2 2 之外,其余质数只需要与相邻的质数合并即可。
我们可以用一个变量 c n t cnt cnt 记录当前连续的质数个数,遇到偶数就将 c n t cnt cnt 清零,遇到奇数质数就将 c n t cnt cnt 加 1 1 1。每遇到一个偶数,就将之前的连续质数个数除以 2 2 2 加到答案中,然后 c n t cnt cnt 清零。
最后再将剩余的 c n t cnt cnt 除以 2 2 2 加到答案中即可。
参考代码
- Python
import math
def is_prime(x):
if x < 2:
return False
for i in range(2, int(math.sqrt(x)) + 1):
if x % i == 0:
return False
return True
n = int(input())
a = list(map(int, input().split()))
ans = 0
cnt = 0
for i in range(n):
if a[i] == 2:
if i > 0 and is_prime(a[i-1]) and is_prime(a[i]+a[i-1]):
cnt += 2
elif i < n-1 and is_prime(a[i+1]) and is_prime(a[i]+a[i+1]):
cnt += 2
elif is_prime(a[i]):
cnt += 1
else:
ans += cnt // 2
cnt = 0
ans += cnt // 2
print(n - ans)
- Java
import java.io.*;
import java.util.*;
public class Main {
static boolean isPrime(int x) {
if (x < 2) return false;
for (int i = 2; i * i <= x; i++) {
if (x % i == 0) return false;
}
return true;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
int ans = 0, cnt = 0;
for (int i = 0; i < n; i++) {
if (a[i] == 2) {
if (i > 0 && isPrime(a[i-1]) && isPrime(a[i]+a[i-1])) {
cnt += 2;
} else if (i < n-1 && isPrime(a[i+1]) && isPrime(a[i]+a[i+1])) {
cnt += 2;
}
} else if (isPrime(a[i])) {
cnt++;
} else {
ans += cnt / 2;
cnt = 0;
}
}
ans += cnt / 2;
System.out.println(n - ans);
}
}
- Cpp
#include <iostream>
using namespace std;
bool isPrime(int x) {
if (x < 2) return false;
for (int i = 2; i * i <= x; i++) {
if (x % i == 0) return false;
}
return true;
}
int main() {
int n;
cin >> n;
int a[n];
for (int i = 0; i < n; i++) {
cin >> a[i];
}
int ans = 0, cnt = 0;
for (int i = 0; i < n; i++) {
if (a[i] == 2) {
if (i > 0 && isPrime(a[i-1]) && isPrime(a[i]+a[i-1])) {
cnt += 2;
} else if (i < n-1 && isPrime(a[i+1]) && isPrime(a[i]+a[i+1])) {
cnt += 2;
}
} else if (isPrime(a[i])) {
cnt++;
} else {
ans += cnt / 2;
cnt = 0;
}
}
ans += cnt / 2;
cout << n - ans << endl;
return 0;
}
💊 04.卢小姐的树园直径测算
问题描述
卢小姐是一位热爱自然的园艺师,她在自己的园子里种植了一种非常特殊的树,这些树的生长模式形成了一个巨大的树形网络。在一个悠闲的午后,卢小姐想要测量这个网络的直径。在这里,我们定义树的直径为树中任意两个节点之间距离的最大值。为了更好地了解这个网络,卢小姐决定在每个节点上尝试种植一个新的树苗,并观察这对树的直径的影响。
我们用 f ( i ) f(i) f(i) 来表示在第 i i i 个节点上添加一个新的树苗后,树的直径的长度。卢小姐希望你能帮她计算出从 f ( 1 ) f(1) f(1) 到 f ( n ) f(n) f(n) 的所有值。
输入格式
第一行包含一个正整数 n n n,代表树的节点数量。
接下来的 n − 1 n - 1 n−1 行,每行包含两个正整数 u , v u, v u,v,代表节点 u u u 和节点 v v v 之间有一条长度为 1 1 1 的边连接。
输出格式
输出共 n n n 行,第 i i i 行包含一个整数,代表 f ( i ) f(i) f(i) 的值。
样例输入
5
1 2
2 3
3 4
2 5
样例输出
4
3
3
4
4
数据范围
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105, 1 ≤ u , v ≤ n 1 \leq u, v \leq n 1≤u,v≤n。
题解
在这个问题中,我们首先需要找到树的直径。为此,我们可以选择任意一个节点作为起点,进行一次深度优先搜索(DFS),找到距离它最远的节点。然后,以这个最远的节点为起点,再进行一次 DFS,找到距离它最远的节点,这两个节点之间的距离即为树的直径。
当我们在某个节点上添加一个新的树苗时,树的直径可能会增加,也可能保持不变。如果这个节点在原有直径的路径上,那么直径不会改变;如果不在,那么直径可能会增加。因此,我们需要计算每个节点到原直径两端的距离,取这些距离中的最大值加 1 1 1,与原直径长度比较,取最大值即为 f ( i ) f(i) f(i)。
参考代码
- Python
import sys
sys.setrecursionlimit(10**7)
def calculate_diameter():
n = int(input())
paths = [[] for _ in range(n + 1)]
for _ in range(n - 1):
u, v = map(int, input().split())
paths[u].append(v)
paths[v].append(u)
def dfs(node, parent, distance):
for neighbor in paths[node]:
if neighbor == parent:
continue
distance[neighbor] = distance[node] + 1
dfs(neighbor, node, distance)
distance_from_root = [0] * (n + 1)
distance_from_first_end = [0] * (n + 1)
distance_from_second_end = [0] * (n + 1)
dfs(1, 0, distance_from_root)
first_end, _ = max(enumerate(distance_from_root), key=lambda x: x[1])
dfs(first_end, 0, distance_from_first_end)
second_end, diameter_length = max(enumerate(distance_from_first_end), key=lambda x: x[1])
dfs(second_end, 0, distance_from_second_end)
for i in range(1, n + 1):
f_i = max(diameter_length, distance_from_second_end[i] + 1, distance_from_first_end[i] + 1)
print(f_i)
calculate_diameter()
- Java
import java.util.*;
public class TreeDiameter {
static List<List<Integer>> paths;
static int[] distanceFromRoot, distanceFromFirstEnd, distanceFromSecondEnd;
static int n;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
paths = new ArrayList<>();
for (int i = 0; i <= n; i++) {
paths.add(new ArrayList<>());
}
for (int i = 1; i < n; i++) {
int u = sc.nextInt();
int v = sc.nextInt();
paths.get(u).add(v);
paths.get(v).add(u);
}
distanceFromRoot = new int[n + 1];
distanceFromFirstEnd = new int[n + 1];
distanceFromSecondEnd = new int[n + 1];
calculateDiameter();
}
static void dfs(int node, int parent, int[] distance) {
for (int neighbor : paths.get(node)) {
if (neighbor != parent) {
distance[neighbor] = distance[node] + 1;
dfs(neighbor, node, distance);
}
}
}
static void calculateDiameter() {
dfs(1, 0, distanceFromRoot);
int firstEnd = 0, maxDistance = 0;
for (int i = 1; i <= n; i++) {
if (distanceFromRoot[i] > maxDistance) {
maxDistance = distanceFromRoot[i];
firstEnd = i;
}
}
dfs(firstEnd, 0, distanceFromFirstEnd);
int secondEnd = 0;
maxDistance = 0;
for (int i = 1; i <= n; i++) {
if (distanceFromFirstEnd[i] > maxDistance) {
maxDistance = distanceFromFirstEnd[i];
secondEnd = i;
}
}
int diameterLength = maxDistance;
dfs(secondEnd, 0, distanceFromSecondEnd);
for (int i = 1; i <= n; i++) {
int f_i = Math.max(diameterLength, Math.max(distanceFromSecondEnd[i] + 1, distanceFromFirstEnd[i] + 1));
System.out.println(f_i);
}
}
}
- Cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
vector<int> paths[MAXN];
int distanceFromRoot[MAXN], distanceFromFirstEnd[MAXN], distanceFromSecondEnd[MAXN];
int n;
void dfs(int node, int parent, int distance[]) {
for (int neighbor : paths[node]) {
if (neighbor != parent) {
distance[neighbor] = distance[node] + 1;
dfs(neighbor, node, distance);
}
}
}
void calculateDiameter() {
dfs(1, 0, distanceFromRoot);
pair<int, int> firstEnd = {0, 0};
for (int i = 1; i <= n; ++i) {
if (distanceFromRoot[i] > firstEnd.second) {
firstEnd = {i, distanceFromRoot[i]};
}
}
dfs(firstEnd.first, 0, distanceFromFirstEnd);
pair<int, int> secondEnd = {0, 0};
for (int i = 1; i <= n; ++i) {
if (distanceFromFirstEnd[i] > secondEnd.second) {
secondEnd = {i, distanceFromFirstEnd[i]};
}
}
int diameterLength = secondEnd.second;
dfs(secondEnd.first, 0, distanceFromSecondEnd);
for (int i = 1; i <= n; ++i) {
int f_i = max(diameterLength, max(distanceFromSecondEnd[i] + 1, distanceFromFirstEnd[i] + 1));
cout << f_i << endl;
}
}
int main() {
cin >> n;
for (int i = 1; i < n; ++i) {
int u, v;
cin >> u >> v;
paths[u].push_back(v);
paths[v].push_back(u);
}
calculateDiameter();
return 0;
}
写在最后
📧 KK这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 KK领取,会在飞书进行同步的跟新,5月1日之前限时免费提供ing~