🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员
✨ 本系列打算持续跟新华为近期的春秋招笔试题汇总~
💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导
👏 感谢大家的订阅➕ 和 喜欢💗
📧 清隆这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 清隆领取,会在飞书进行同步的跟新。
文章目录
01.K小姐的图书馆管理系统
问题描述
K小姐是某大学图书馆的管理员,她正在设计一个高效的图书管理系统。在这个系统中,需要实现一个缓存管理策略来提高图书的访问效率。这个缓存管理策略使用的是 LRU(最近最少使用)算法。
LRU 缓存算法是一种常用于管理缓存的策略,其目标是保留最近使用过的数据,淘汰最久未被使用的数据。实现一个简单的 LRU 缓存算法,支持查询、插入和删除操作。
最久未被使用的定义:查询、插入和删除操作均为一次访问操作,每个元素均有一个最后一次被访问时间,按照最后一次被访问时间排序,时间最早的即为最久未使用。插入操作:当缓存中已经存在,则刷新值;不存在,则插入;如果超过上限,则淘汰最久未被使用的元素。
输入格式
第一行包含两个整数 N N N 和 K K K,分别表示缓存内最多可以存放的页数,以及操作序列中的总操作数。其中 N ∈ [ 1 , 100 ] N \in [1,100] N∈[1,100], K ∈ [ 1 , 10000 ] K \in [1,10000] K∈[1,10000]。
接下来的 K K K 行,每行两个输入,以空格分隔。第一个输入是一个字符,表示操作类型: A A A 表示插入, Q Q Q 表示查询, D D D 表示删除。第二个输入是一个整数,表示页面编号。其中页面编号的范围为 [ 1 , 100000 ] [1,100000] [1,100000]。
输出格式
输出一行,表示缓存内各页面的编号,按照从小到大排序。
样例输入
2 5
A 103
A 102
A 102
Q 103
A 101
样例输出
101 103
数据范围
- 操作数 K K K 的范围: 1 ≤ K ≤ 10000 1 \leq K \leq 10000 1≤K≤10000
- 缓存页数 N N N 的范围: 1 ≤ N ≤ 100 1 \leq N \leq 100 1≤N≤100
- 页面编号的范围: 1 ≤ 页面编号 ≤ 100000 1 \leq 页面编号 \leq 100000 1≤页面编号≤100000
题解
这道题使用双向链表和哈希表来实现 LRU 缓存算法。
首先,使用一个双向链表来维护页面的访问顺序。链表头部表示最近使用的页面,尾部表示最久未使用的页面。每次查询或插入页面时,都将该页面移动到链表头部。
为了加快页面的查找速度,我们使用一个哈希表来存储页面编号和对应的链表节点。这样每次查找、插入和删除操作的时间复杂度都是 O ( 1 ) O(1) O(1)。
当插入新的页面时,如果缓存已满,需要删除链表尾部的页面(即最久未使用的页面),然后将新页面插入到链表头部。
具体实现请参考以下代码:
参考代码
- Python
class DoubleLinkedNode:
def __init__(self, key=0, value=0):
self.key = key
self.value = value
self.prev = None
self.next = None
class LRUCache:
def __init__(self, cap):
self.cache = dict()
self.head = DoubleLinkedNode()
self.tail = DoubleLinkedNode()
self.head.next = self.tail
self.tail.prev = self.head
self.cap = cap
self.size = 0
def output(self):
temp = self.head.next
ans = []
while temp and temp != self.tail:
ans.append(temp.value)
temp = temp.next
print(*sorted(ans))
def get(self, key):
if key not in self.cache:
return -1
node = self.cache[key]
self.move_to_head(node)
return node.value
def put(self, key, value):
if key not in self.cache:
node = DoubleLinkedNode(key, value)
self.cache[key] = node
self.add_to_head(node)
self.size += 1
if self.size > self.cap:
removed = self.remove_tail()
self.cache.pop(removed.key)
self.size -= 1
else:
node = self.cache[key]
node.value = value
self.move_to_head(node)
def add_to_head(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def remove_node(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def move_to_head(self, node):
self.remove_node(node)
self.add_to_head(node)
def remove_tail(self):
node = self.tail.prev
self.remove_node(node)
return node
ans = []
cap, q = map(int, input().split())
lrucache = LRUCache(cap)
for i in range(q):
line = input().split()
op, key = line[0], int(line[1])
if op == 'Q':
lrucache.get(key)
else:
lrucache.put(key, key)
lrucache.output()
- Java
import java.util.*;
class DoubleLinkedNode {
int key;
int value;
DoubleLinkedNode prev;
DoubleLinkedNode next;
public DoubleLinkedNode() {}
public DoubleLinkedNode(int key, int value) {
this.key = key;
this.value = value;
}
}
class LRUCache {
private Map<Integer, DoubleLinkedNode> cache = new HashMap<>();
private int size;
private int capacity;
private DoubleLinkedNode head, tail;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
head = new DoubleLinkedNode();
tail = new DoubleLinkedNode();
head.next = tail;
tail.prev = head;
}
public void output() {
List<Integer> ans = new ArrayList<>();
DoubleLinkedNode temp = head.next;
while (temp != null && temp != tail) {
ans.add(temp.value);
temp = temp.next;
}
Collections.sort(ans);
for (int val : ans) {
System.out.print(val + " ");
}
}
public int get(int key) {
DoubleLinkedNode node = cache.get(key);
if (node == null) {
return -1;
}
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DoubleLinkedNode node = cache.get(key);
if (node == null) {
DoubleLinkedNode newNode = new DoubleLinkedNode(key, value);
cache.put(key, newNode);
addToHead(newNode);
size++;
if (size > capacity) {
DoubleLinkedNode tail = removeTail();
cache.remove(tail.key);
size--;
}
} else {
node.value = value;
moveToHead(node);
}
}
private void addToHead(DoubleLinkedNode node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(DoubleLinkedNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveToHead(DoubleLinkedNode node) {
removeNode(node);
addToHead(node);
}
private DoubleLinkedNode removeTail() {
DoubleLinkedNode res = tail.prev;
removeNode(res);
return res;
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int capacity = scanner.nextInt();
int queryCount = scanner.nextInt();
LRUCache lruCache = new LRUCache(capacity);
for (int i = 0; i < queryCount; i++) {
String op = scanner.next();
int key = scanner.nextInt();
if (op.equals("Q")) {
lruCache.get(key);
} else {
lruCache.put(key, key);
}
}
lruCache.output();
}
}
#include <iostream>
#include <unordered_map>
#include <list>
#include <vector>
#include <algorithm>
using namespace std;
class LRUCache {
public:
LRUCache(int capacity) : capacity(capacity) {}
void put(int key, int value) {
auto it = cache.find(key);
if (it != cache.end()) {
cache.erase(it);
keys.remove(key);
} else if (keys.size() >= capacity) {
int lru = keys.back();
keys.pop_back();
cache.erase(lru);
}
keys.push_front(key);
cache[key] = { value, keys.begin() };
}
int get(int key) {
auto it = cache.find(key);
if (it == cache.end()) return -1;
keys.erase(it->second.second);
keys.push_front(key);
cache[key] = { it->second.first, keys.begin() };
return it->second.first;
}
void output() {
vector<int> res(keys.begin(), keys.end());
sort(res.begin(), res.end());
for (int val : res) {
cout << val << " ";
}
cout << endl;
}
private:
int capacity;
list<int> keys;
unordered_map<int, pair<int, list<int>::iterator>> cache;
};
int main() {
int capacity, queryCount;
cin >> capacity >> queryCount;
LRUCache lruCache(capacity);
for (int i = 0; i < queryCount; ++i) {
char op;
int key;
cin >> op >> key;
if (op == 'Q') {
lruCache.get(key);
} else {
lruCache.put(key, key);
}
}
lruCache.output();
return 0;
}
02.K 小姐的正则表达式实验
问题描述
K 小姐是一名计算机专业的大学生,这学期她选修了一门编译原理课程。这门课的实验作业十分繁重,其中有一个实验要求利用正则表达式对文本进行切词处理。已知存在一种字符串解析语法,其中的语法元素如下:
N
:用于匹配单个数字( 0 0 0- 9 9 9)。A
:用于匹配单个字母( a a a- z z z, A A A- Z Z Z)。n()
:用于表示一个分组,分组中至少有一个N
语法元素或者A
语法元素, n n n 为一个数值,表示匹配 n n n 次, 1 ≤ n ≤ 200 1 \leq n \leq 200 1≤n≤200。
给定解析语法和目标字符串,要求从目标字符串中找到第一个满足解析语法的子串。
输入格式
输入两行数据,第一行是给定的解析语法,第二行是目标字符串。
输出格式
输出匹配的子字符串内容,如果没有匹配中任何字符串,输出 !
(英文感叹号)。
样例输入
2(AN)
BA3A3ABB
样例输出
A3A3
样例输入
2(A2(N))
A3322A33P20BB
样例输出
A33P20
数据范围
- 解析语法中的 n n n 满足 1 ≤ n ≤ 200 1 \leq n \leq 200 1≤n≤200。
- 目标字符串的长度不超过 1 0 5 10^5 105。
题解
这道题目要求我们根据给定的解析语法,从目标字符串中找到第一个满足条件的子串。解析语法中包含了数字和字母的匹配规则,以及分组和重复的规则。我们需要先将解析语法转换成一个具体的正则表达式,然后在目标字符串中进行匹配。
具体步骤如下:
- 解析输入的解析语法,将其转换成一个具体的正则表达式。
- 使用转换后的正则表达式在目标字符串中进行匹配。
- 输出第一个匹配的子串,如果没有匹配到则输出
!
。
参考代码
- Python
import re
def parse_pattern(pattern):
stack = []
i = 0
while i < len(pattern):
if pattern[i].isdigit():
num = 0
while i < len(pattern) and pattern[i].isdigit():
num = num * 10 + int(pattern[i])
i += 1
stack.append(num)
elif pattern[i] == '(':
stack.append('(')
i += 1
elif pattern[i] == ')':
sub_pattern = []
while stack and stack[-1] != '(':
sub_pattern.append(stack.pop())
stack.pop() # pop '('
sub_pattern.reverse()
repeat = stack.pop()
stack.append(''.join(sub_pattern) * repeat)
i += 1
else:
stack.append(pattern[i])
i += 1
return ''.join(stack)
def match_pattern(pattern, string):
regex = parse_pattern(pattern)
regex = regex.replace('A', '[a-zA-Z]').replace('N', '[0-9]')
match = re.search(regex, string)
if match:
return match.group(0)
else:
return '!'
if __name__ == "__main__":
pattern = input().strip()
string = input().strip()
result = match_pattern(pattern, string)
print(result)
- Java
import java.util.Scanner;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String pattern = scanner.nextLine().trim();
String string = scanner.nextLine().trim();
scanner.close();
String regex = parsePattern(pattern);
regex = regex.replace("A", "[a-zA-Z]").replace("N", "[0-9]");
Pattern compiledPattern = Pattern.compile(regex);
Matcher matcher = compiledPattern.matcher(string);
if (matcher.find()) {
System.out.println(matcher.group(0));
} else {
System.out.println("!");
}
}
private static String parsePattern(String pattern) {
Stack<Object> stack = new Stack<>();
int i = 0;
while (i < pattern.length()) {
if (Character.isDigit(pattern.charAt(i))) {
int num = 0;
while (i < pattern.length() && Character.isDigit(pattern.charAt(i))) {
num = num * 10 + (pattern.charAt(i) - '0');
i++;
}
stack.push(num);
} else if (pattern.charAt(i) == '(') {
stack.push('(');
i++;
} else if (pattern.charAt(i) == ')') {
StringBuilder subPattern = new StringBuilder();
while (!stack.isEmpty() && stack.peek() instanceof String) {
subPattern.insert(0, stack.pop());
}
stack.pop(); // pop '('
int repeat = (int) stack.pop();
String repeatedPattern = subPattern.toString().repeat(repeat);
stack.push(repeatedPattern);
i++;
} else {
stack.push(String.valueOf(pattern.charAt(i)));
i++;
}
}
StringBuilder result = new StringBuilder();
for (Object obj : stack) {
result.append(obj);
}
return result.toString();
}
}
- Cpp
#include <bits/stdc++.h>
using namespace std;
string parsePattern(const string &pattern) {
stack<int> cnt;
stack<string> stk;
string tmp;
for (int i = 0; i < pattern.size(); ++i) {
if (isdigit(pattern[i])) {
int sum = 0;
while (i < pattern.size() && isdigit(pattern[i])) {
sum = sum * 10 + pattern[i] - '0';
++i;
}
cnt.push(sum);
stk.push(tmp);
tmp.clear();
} else if (pattern[i] == ')') {
string subPattern = tmp;
tmp = stk.top();
stk.pop();
int repeat = cnt.top();
cnt.pop();
while (repeat--) {
tmp += subPattern;
}
} else if (pattern[i] != '(') {
tmp += pattern[i];
}
}
return tmp;
}
bool check(char a, char b) {
if (b == 'A' && isalpha(a)) return true;
if (b == 'N' && isdigit(a)) return true;
return false;
}
int main() {
string t, s;
cin >> t >> s;
string pattern = parsePattern(t);
pattern = regex_replace(pattern, regex("A"), "[a-zA-Z]");
pattern = regex_replace(pattern, regex("N"), "[0-9]");
regex re(pattern);
smatch match;
if (regex_search(s, match, re)) {
cout << match.str(0) << endl;
} else {
cout << "!" << endl;
}
return 0;
}
03.K小姐的网络设备安装挑战
问题描述
K小姐最近接到了一项任务,需要为客户安装两台网络设备。每台设备需要配备接口板才能正常工作,接口板用于接入用户业务,且接口板的转发能力之和不能超过设备的整机转发能力。客户订购了两台设备和若干块接口板。K小姐需要计算是否存在一种安装方法,使得所有接口板刚好能装到两台设备上,且每台设备配置的接口板的转发能力之和刚好等于整机的转发能力。
输入格式
第一行是设备的整机转发能力 m m m,单位为 Gbps。
第二行是客户订购的接口板数量 n n n。
第三行是包含 n n n 个接口板的转发能力的列表。
输出格式
如果存在满足要求的安装方法,请分两行输出两台设备配置的接口板的转发能力的列表,且要求每台设备的接口板按转发能力从小到大排列。两台设备的接口板,第一个接口板转发能力小的优先输出。如果第一个接口板转发能力相同,那接口板数量多的优先输出。如果不存在对应的安装方案,则返回 -1。
样例输入
100
5
40 10 10 40 100
样例输出
10 10 40 40
100
数据范围
- 设备整机转发能力 m m m 的范围为 [1, 2000]。
- 接口板数量 n n n 的范围为 [1, 200]。
- 接口板转发能力为正整数。
题解
这道题目可以通过动态规划来解决。我们需要找到一种方法将接口板分成两组,每组的转发能力之和等于设备的整机转发能力。具体步骤如下:
- 首先,检查所有接口板的转发能力之和是否等于 2 m 2m 2m,如果不等于,直接返回 -1。
- 使用动态规划来判断是否可以将接口板分成两组,每组的转发能力之和等于 m m m。
- 如果可以分成两组,输出两组接口板的转发能力列表,并按照题目要求进行排序和输出。
其中 dp[j]
表示是否可以通过选择若干个接口板使得它们的转发能力之和为
j
j
j。初始时,dp = 0
,表示不选择任何接口板时,转发能力之和为 0。我们还需要一个二维数组 pre
来记录每个状态的前驱状态,以便于最后回溯找到具体的接口板组合。具体步骤如下:
- 初始化
dp
数组和pre
数组。 - 遍历每个接口板,对于每个接口板,更新
dp
数组和pre
数组。 - 最后检查
dp[m]
是否为 -1,如果是,表示无法找到满足条件的组合,返回 -1。 - 否则,通过
pre
数组回溯找到具体的接口板组合,并输出结果。
参考代码
- Python
def solve():
max_capacity = int(input())
num_boards = int(input())
board_capacities = list(map(int, input().split()))
if sum(board_capacities) != 2 * max_capacity:
print(-1)
return
dp = [-1] * (max_capacity + 1)
dp[0] = 0
pre = [[-1] * (max_capacity + 1) for _ in range(num_boards)]
for i in range(num_boards):
for j in range(max_capacity, board_capacities[i] - 1, -1):
if dp[j - board_capacities[i]] != -1:
dp[j] = i
pre[i][j] = dp[j - board_capacities[i]]
if dp[max_capacity] == -1:
print(-1)
return
used = [False] * num_boards
i, j = max_capacity, dp[max_capacity]
while i > 0:
used[j] = True
i -= board_capacities[j]
j = pre[j][i]
device1 = [board_capacities[i] for i in range(num_boards) if used[i]]
device2 = [board_capacities[i] for i in range(num_boards) if not used[i]]
device1.sort()
device2.sort()
if device1[0] > device2[0] or (device1[0] == device2[0] and len(device1) < len(device2)):
device1, device2 = device2, device1
print(" ".join(map(str, device1)))
print(" ".join(map(str, device2)))
solve()
- Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int maxCapacity = sc.nextInt();
int numBoards = sc.nextInt();
int[] boardCapacities = new int[numBoards];
for (int i = 0; i < numBoards; i++) {
boardCapacities[i] = sc.nextInt();
}
if (Arrays.stream(boardCapacities).sum() != 2 * maxCapacity) {
System.out.println(-1);
return;
}
int[] dp = new int[maxCapacity + 1];
Arrays.fill(dp, -1);
dp[0] = 0;
int[][] pre = new int[numBoards][maxCapacity + 1];
for (int[] row : pre) {
Arrays.fill(row, -1);
}
for (int i = 0; i < numBoards; i++) {
for (int j = maxCapacity; j >= boardCapacities[i]; j--) {
if (dp[j - boardCapacities[i]] != -1) {
dp[j] = i;
pre[i][j] = dp[j - boardCapacities[i]];
}
}
}
if (dp[maxCapacity] == -1) {
System.out.println(-1);
return;
}
boolean[] used = new boolean[numBoards];
int i = maxCapacity, j = dp[maxCapacity];
while (i > 0) {
used[j] = true;
i -= boardCapacities[j];
j = pre[j][i];
}
List<Integer> device1 = new ArrayList<>();
List<Integer> device2 = new ArrayList<>();
for (int k = 0; k < numBoards; k++) {
if (used[k]) {
device1.add(boardCapacities[k]);
} else {
device2.add(boardCapacities[k]);
}
}
Collections.sort(device1);
Collections.sort(device2);
if (device1.get(0) > device2.get(0) || (device1.get(0).equals(device2.get(0)) && device1.size() < device2.size())) {
List<Integer> temp = device1;
device1 = device2;
device2 = temp;
}
for (int k = 0; k < device1.size(); k++) {
System.out.print(device1.get(k));
if (k < device1.size() - 1) {
System.out.print(" ");
}
}
System.out.println();
for (int k = 0; k < device2.size(); k++) {
System.out.print(device2.get(k));
if (k < device2.size() - 1) {
System.out.print(" ");
}
}
System.out.println();
}
}
- Cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int max_capacity, num_boards;
cin >> max_capacity >> num_boards;
vector<int> board_capacities(num_boards);
for (int i = 0; i < num_boards; ++i) cin >> board_capacities[i];
if (accumulate(board_capacities.begin(), board_capacities.end(), 0) != max_capacity * 2) {
cout << -1 << endl;
return 0;
}
vector<vector<int>> previous(num_boards, vector<int>(max_capacity + 1, -1));
vector<int> dp(max_capacity + 1, -1);
dp[0] = 0;
for (int i = 0; i < num_boards; ++i) {
for (int j = max_capacity; j >= board_capacities[i]; --j) {
if (dp[j - board_capacities[i]] != -1) {
previous[i][j] = dp[j - board_capacities[i]];
dp[j] = i;
}
}
}
if (dp[max_capacity] == -1) {
cout << -1 << endl;
return 0;
}
vector<bool> used(num_boards, false);
for (int i = max_capacity, j = dp[max_capacity]; i > 0;) {
used[j] = true;
int capacity = board_capacities[j];
j = previous[j][i];
i -= capacity;
}
vector<int> device1, device2;
for (int i = 0; i < num_boards; ++i) {
if (used[i]) device1.push_back(board_capacities[i]);
else device2.push_back(board_capacities[i]);
}
sort(device1.begin(), device1.end());
sort(device2.begin(), device2.end());
if (device1[0] > device2[0] || (device1[0] == device2[0] && device1.size() < device2.size())) swap(device1, device2);
for (int i = 0; i < device1.size(); ++i) cout << device1[i] << " \n"[i == device1.size() - 1];
for (int i = 0; i < device2.size(); ++i) cout << device2[i] << " \n"[i == device2.size() - 1];
return 0;
}
写在最后
📧 清隆这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 清隆领取,会在飞书进行同步的跟新。