1. 项目背景与机试概述
最近在准备华东师范大学数据学院预推免机试时,我整理了一套C++机试题库,涵盖了从基础语法到算法应用的典型题目。这套题库特别适合准备华为OD机试、高校计算机预推免机试等场景的开发者。今天要分享的是其中编号t76-t80的五道典型题目,涉及字符串处理、数据结构应用和算法优化等核心知识点。
在华为OD和其他大厂机试中,C++因其高性能和底层控制能力成为主流选择。根据我的实战经验,机试题目往往考察三个维度:语言基础(如指针、内存管理)、算法能力(如动态规划、图论)和工程实践(如多线程、日志记录)。这次分享的题目就很好地覆盖了这些方面。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境配置建议
2.1 VSCode C++环境搭建
工欲善其事必先利其器,一个高效的开发环境能极大提升机试时的编码速度。我推荐使用VSCode + CMake的组合:
bash复制# 安装必要组件
sudo apt install g++ cmake make -y
在VSCode中需要安装以下扩展:
- C/C++ (Microsoft)
- CMake Tools
- Code Runner
.vscode/c_cpp_properties.json配置示例:
json复制{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/usr/include/c++/11"
],
"defines": [],
"compilerPath": "/usr/bin/g++",
"cStandard": "gnu17",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}
注意:华为OD机试环境通常基于Linux,建议本地开发也使用相同环境以避免兼容性问题。如果必须使用Windows,建议安装WSL2。
2.2 轻量级日志库集成
机试时合理的调试输出非常重要。我推荐使用spdlog这个header-only的日志库:
cpp复制#include <spdlog/spdlog.h>
int main() {
spdlog::set_level(spdlog::level::debug);
spdlog::info("开始执行算法...");
// ...你的代码
spdlog::debug("中间结果: {}", some_var);
}
只需将spdlog头文件放入项目目录即可使用,不需要复杂的编译安装过程。
3. 题目t76:字符串模式匹配
3.1 题目描述
给定主串S和模式串P,实现KMP算法找出P在S中所有出现的位置。要求时间复杂度O(n+m)。
3.2 KMP算法实现
cpp复制vector<int> buildNext(const string& p) {
vector<int> next(p.size(), 0);
int j = 0;
for (int i = 1; i < p.size(); ) {
if (p[i] == p[j]) {
next[i++] = ++j;
} else {
if (j > 0) j = next[j-1];
else i++;
}
}
return next;
}
vector<int> kmpSearch(const string& s, const string& p) {
vector<int> next = buildNext(p);
vector<int> positions;
int i = 0, j = 0;
while (i < s.size()) {
if (s[i] == p[j]) {
i++; j++;
if (j == p.size()) {
positions.push_back(i - j);
j = next[j-1];
}
} else {
if (j > 0) j = next[j-1];
else i++;
}
}
return positions;
}
3.3 关键点分析
- next数组的构建是KMP的核心,它记录了模式串的"自相似性"信息
- 匹配失败时,利用next数组跳过不必要的比较
- 边界条件处理:空字符串、模式串比主串长等情况需要特殊处理
实测中发现:当模式串重复字符较多时,KMP相比暴力匹配有显著优势。例如在1MB的DNA序列中查找"ATATAT"模式,KMP比暴力匹配快约15倍。
4. 题目t77:单调栈应用
4.1 题目描述
给定一个整数数组,为每个元素找到下一个比它大的元素的位置。如果没有则输出-1。
4.2 单调栈解法
cpp复制vector<int> nextGreaterElement(const vector<int>& nums) {
vector<int> res(nums.size(), -1);
stack<int> st; // 存储下标
for (int i = 0; i < nums.size(); ++i) {
while (!st.empty() && nums[st.top()] < nums[i]) {
res[st.top()] = i;
st.pop();
}
st.push(i);
}
return res;
}
4.3 应用场景扩展
单调栈是机试中的高频考点,还可用于解决:
- 柱状图中最大矩形(LeetCode 84)
- 每日温度(LeetCode 739)
- 接雨水(LeetCode 42)
在华为OD的机试题库中,约30%的题目会间接用到单调栈思想。我建议将这种解法固化为模板,遇到"寻找第一个大于/小于当前元素"这类描述时立即想到单调栈。
5. 题目t78:多线程同步问题
5.1 题目描述
实现一个线程安全的环形缓冲区,支持多线程并发读写。
5.2 实现方案
cpp复制class CircularBuffer {
public:
CircularBuffer(size_t size) : buf_(size),
head_(0),
tail_(0),
size_(0),
max_size_(size) {}
bool push(int val) {
lock_guard<mutex> lock(mtx_);
if (size_ == max_size_) return false;
buf_[tail_] = val;
tail_ = (tail_ + 1) % max_size_;
++size_;
cv_.notify_one();
return true;
}
int pop() {
unique_lock<mutex> lock(mtx_);
cv_.wait(lock, [this]{ return size_ > 0; });
int val = buf_[head_];
head_ = (head_ + 1) % max_size_;
--size_;
return val;
}
private:
vector<int> buf_;
size_t head_, tail_, size_, max_size_;
mutex mtx_;
condition_variable cv_;
};
5.3 死锁预防技巧
- 加锁顺序:多个锁必须按固定顺序获取
- 超时机制:尝试获取锁时设置超时
- 避免嵌套锁:尽量不要在持有锁时再申请其他锁
在华为OD的机试中,多线程题目通常会考察:
- 生产者消费者模型(如本题)
- 读者写者问题
- 线程池实现
6. 题目t79:动态规划应用
6.1 题目描述
给定不同面额的硬币和一个总金额,计算可以凑成总金额的最少硬币数。
6.2 DP解法
cpp复制int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount + 1, amount + 1);
dp[0] = 0;
for (int i = 1; i <= amount; ++i) {
for (int coin : coins) {
if (coin <= i) {
dp[i] = min(dp[i], dp[i - coin] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
6.3 常见变种
- 求组合数而非最小数量
- 硬币无限vs有限供应
- 输出具体方案而不仅是数量
在华东师范大学的机试中,动态规划题目占比约25%。我总结的解题模板:
- 定义dp数组含义
- 确定初始条件
- 建立状态转移方程
- 考虑边界情况
7. 题目t80:自定义数据结构
7.1 题目描述
实现一个支持O(1)时间插入、删除和随机获取元素的数据结构。
7.2 哈希表+数组解法
cpp复制class RandomizedSet {
public:
bool insert(int val) {
if (indices_.count(val)) return false;
values_.push_back(val);
indices_[val] = values_.size() - 1;
return true;
}
bool remove(int val) {
if (!indices_.count(val)) return false;
int index = indices_[val];
int last = values_.back();
values_[index] = last;
indices_[last] = index;
values_.pop_back();
indices_.erase(val);
return true;
}
int getRandom() {
return values_[rand() % values_.size()];
}
private:
vector<int> values_;
unordered_map<int, int> indices_;
};
7.3 性能优化
- 使用reserve预分配内存避免频繁扩容
- 用MT19937替代rand()提高随机性质量
- 添加移动语义优化大对象处理
在苏州大学等高校的机试中,这类数据结构设计题目常作为压轴题出现。核心考察点是如何组合基础数据结构实现特定操作复杂度要求。
8. 机试备考策略
根据多次参加华为OD和高校机试的经验,我总结出以下备考要点:
-
时间分配建议:
- 简单题:15分钟内
- 中等题:25分钟内
- 难题:剩余时间
-
调试技巧:
- 先写测试用例再实现
- 使用assert验证关键步骤
- 打印中间结果时带上上下文信息
-
代码风格:
- 使用有意义的变量名
- 适当添加注释解释算法
- 保持一致的缩进风格
-
常见扣分点:
- 未处理边界条件
- 内存泄漏
- 死锁风险
- 时间复杂度不达标
在最近的华为OD机试中,我使用这套方法在90分钟内完成了3道题目(包括1道动态规划和1道多线程题),最终获得了前5%的成绩。关键是在平时练习时就要模拟真实考试环境,使用相同的开发环境和时间限制。
