2023大厂笔试模拟练习网站(含题解)
www.codefun2000.com
最近我们一直在将收集到的各种大厂笔试的解题思路还原成题目并制作数据,挂载到我们的OJ上,供大家学习交流,体会笔试难度。现已录入200+道互联网大厂模拟练习题,还在极速更新中。欢迎关注公众号“塔子哥学算法”获取最新消息。
提交链接:
https://codefun2000.com/p/P1088
为了更好的阅读体检,可以查看OJ上的题解。进入提交链接,点击右边菜单栏的"查看塔子哥的题解"
目录
1.题面
2.思路
3.类似题目推荐
4.代码
题面
塔子哥是一名设计师,最近接到了一项重要任务:设计一个新款的生日蜡烛,以庆祝公司成立20周年。为了使蜡烛看起来更加炫目,塔子哥计划在蜡烛上缠绕彩带。他买了一串长度为 N N N 的非常漂亮的彩带,每一厘米的彩带上都是一种色彩,但是当他拿到彩带后才发现,彩带上的颜色太多了,超出了他设计中所需要的颜色种类数量。
于是,塔子哥决定从彩带上截取一段,使得这段彩带上的颜色种类不超过 K K K种。但是,他希望这段彩带尽量长,这样才能在蜡烛上缠绕出更加炫目的效果。为了尽快完成设计,他来找你求助,希望你能帮他设计出一种截取方法,使得截取出来的彩带尽可能长,并且颜色种类不超过 K K K种。
输入描述
第一行两个整数 N , K N,K N,K,以空格分开,分别表示彩带有 N N N厘米长,你截取的一段连续的彩带不能超过 K K K种颜色。接下来一行 N N N个整数,每个整数表示一种色彩,相同的整数表示相同的色彩。
1 ≤ N , K ≤ 5000 1≤N,K≤5000 1≤N,K≤5000,彩带上的颜色数字介于 [ 1 , 2000 ] [1,2000] [1,2000]之间
输出描述
一行,一个整数,表示选取的彩带的最大长度。
样例 1 1 1
输入
8 3
1 2 3 2 1 4 5 1
输出
5
说明:
最长的一段彩带是 [ 1 , 2 , 3 , 2 , 1 ] [1,2,3,2,1] [1,2,3,2,1],共 5 5 5厘米。
思路
step1:双指针
分析:题目要求满足性质:区间内颜色种类不超过 k k k的 最长区间。所以假设固定左端点,右端点从左往右移动的过程中,性质是先满足,后不满足。即性质满足单调性 。一切满足单调性的问题都能用双指针解决。
如何维护上述性质:
1,我们对当前区间维护一个桶来记录每个值出现的次数(可以使用数组或者哈希表来实现)。
2.在右端点移动的过程中,如果新增的这个数没在桶出现过,那么区间种类数+1。
3.在左端点移动的过程中,如果删去的这个数在桶中恰好只出现一次,那么区间种类数-1.
由此,我们就能够正确的维护区间种类数 , 通过它来判断区间合法性。
类似题目推荐
leetcode
- 167. 两数之和 II - 输入有序数组
- 344. 反转字符串
- 283. 移动零
- 11. 盛最多水的容器
- 剑指 Offer 48. 最长不含重复字符的子字符串
- 209. 长度最小的子数组
- 493. 翻转对 (难度较高,需要掌握归并排序等高级算法)
CodeFun2000
P1001-华为Od-k优雅阈值
P1007-58同城-2022.11.5-找出符合条件的子字符串
P1071-百度-2023.3.7-第二题-排列数量
代码实现
Python
from collections import defaultdict
# 读入
n , k = list(map(int , input().split()))
a = list(map(int , input().split()))
res = 0
i = 0
j = 0
# 记录每个数出现次数
num_count = defaultdict(int)
# 记录当前区间的不同数的个数
dif = 0
# 双指针 , i 是左端点 , j 是右端点
while j < n:
#将a[j]放入桶中
num_count[a[j]] += 1
# 如果a[j] 从未出现过,则种类++
if num_count[a[j]] == 1:
dif += 1
# 不满足性质,则收缩左端点
while dif > k:
# a[i] 出现一次,那么收缩之后没出现,那么种类数--
if num_count[a[i]] == 1:
dif -= 1
num_count[a[i]] -= 1
i += 1
# 到这里,我们就获得了<右端点以j为结尾的,最左的,满足性质的左端点i>. 记录答案
res = max(res , j - i + 1)
j += 1
print (ans)
C++
#include <iostream>
#include <unordered_map> // 引入无序哈希映射库
using namespace std;
int main() {
int n, k;
// 读入n和k
cin >> n >> k;
// 开辟数组a,长度为n,存储已经读入的数组元素
int* a = new int[n];
for (int i = 0; i < n; i++) {
cin >> a[i];
}
// 变量res表示当前的答案,i和j分别是左右指针,dif表示当前区间不同数字的个数
int res = 0, i = 0, j = 0, dif = 0;
// 声明一个哈希映射num_count,存储每个数字出现的次数
unordered_map<int, int> num_count;
// 在哈希映射中将a[j]出现的次数加1
while (j < n) {
num_count[a[j]]++;
// 如果a[j]之前没有出现过
if (num_count[a[j]] == 1) {
// 区间内不同数字的数量+1
dif++;
}
// 如果区间内不同数字的数量>dif,说明不能满足性质,需要将区间收缩
while (dif > k) {
// 如果a[i]只出现过一次
if (num_count[a[i]] == 1) {
// 区间内不同数字的数量-1
dif--;
}
// 在哈希映射中将a[i]出现的次数减1
num_count[a[i]]--;
// 左指针i右移(收缩)
i++;
}
// 更新答案,即记录当前区间长度
res = max(res, j - i + 1);
// 右指针j右移
j++;
}
// 输出当前的答案
cout << res << endl;
// 释放数组a占用的内存
delete[] a;
return 0;
}
Java
import java.util.Scanner;
import java.util.HashMap;
class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int k = sc.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
int res = 0, i = 0, j = 0, dif = 0;
HashMap<Integer, Integer> num_count = new HashMap<>();
while (j < n) {
int cur = a[j];
num_count.put(cur, num_count.getOrDefault(cur, 0) + 1);
// 如果a[j]从未出现过,则种类++
if (num_count.get(cur) == 1) {
dif++;
}
// 不满足性质,则收缩左端点
while (dif > k) {
int c = a[i];
// a[i]出现一次,则种类数--
if (num_count.get(c) == 1) {
dif--;
}
num_count.put(c, num_count.get(c) - 1);
i++;
}
// 记录答案
res = Math.max(res, j - i + 1);
j++;
}
System.out.println(res);
}
}
Js
process.stdin.resume();
process.stdin.setEncoding('utf-8');
let input = '';
process.stdin.on('data', (data) => {
input += data;
return;
});
process.stdin.on('end', () => {
const lines = input.trim().split('\n');
let [n, k] = lines[0].trim().split(' ').map(Number);
let a = lines[1].trim().split(' ').map(Number);
let res = 0;
let i = 0;
let j = 0;
// 记录每个数出现次数
let numCount = new Map();
// 记录当前区间的不同数的个数
let dif = 0;
// 双指针 , i 是左端点 , j 是右端点
while (j < n) {
//将a[j]放入桶中
numCount.set(a[j], (numCount.get(a[j]) || 0) + 1);
// 如果a[j] 从未出现过,则种类++
if (numCount.get(a[j]) === 1) {
dif += 1;
}
// 不满足性质,则收缩左端点
while (dif > k) {
// a[i] 出现一次,那么收缩之后没出现,那么种类数--
if (numCount.get(a[i]) === 1) {
dif -= 1;
}
numCount.set(a[i], numCount.get(a[i]) - 1);
i += 1;
}
// 到这里,我们就获得了<右端点以j为结尾的,最左的,满足性质的左端点i>. 记录答案
res = Math.max(res, j - i + 1);
j += 1;
}
console.log(res); // 输出答案
});
Go
package main
import "fmt"
func main() {
var n, k int
fmt.Scan(&n, &k)
a := make([]int, n)
for i := 0; i < n; i++ {
fmt.Scan(&a[i])
}
numCount := make(map[int]int)
res, i, j, dif := 0, 0, 0, 0
for j < n {
cur := a[j]
numCount[cur]++
if numCount[cur] == 1 { // 如果a[j]从未出现过,则种类++
dif++
}
for dif > k { // 不满足性质,则收缩左端点
c := a[i]
if numCount[c] == 1 { // a[i]出现一次,则种类数--
dif--
}
numCount[c]--
i++
}
res = max(res, j-i+1) // 记录答案
j++
}
fmt.Println(res)
}
func max(a, b int) int { // 自定义max函数
if a > b {
return a
}
return b
}