机试备考笔记 17/31
2025年8月17日
小结:省流,Medium 8 道(08.22 0.20 才在写17号的,这辈子是真要有了
目录
- LeetCode
- 394. 字符串解码
- 399. 除法求值
- 347. 前K个高频元素
- 337. 打家劫舍III
- 309. 买卖股票的最佳时机含冷冻期
- 300. 最长递增子序列
- 287. 寻找重复数
- 279. 完全平方数
- Acwing
- xxx
LeetCode
394. 字符串解码
394. 字符串解码
题目
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
测试用例保证输出的长度不会超过 105。
ez,但是写的不太优雅,用 stack 会好看许多
(22日记,我真的好喜欢从递归里多传东西回来啊,对传的参数用 &
真是小 case
class Solution {
public:string solve(string &s, int begin_idx, int &end_idx) {string ans = "";for (int idx = begin_idx; idx < s.length(); idx++) {if (s[idx] >= '0' && s[idx] <= '9') {int num = s[idx] - '0';idx++;while (s[idx] >= '0' && s[idx] <= '9') {num = num * 10 + s[idx] - '0';idx++;}// 退出 while,是因为当前 s[idx]="["string sub = solve(s, idx + 1, end_idx);for (int k = 0; k < num; k++) {ans += sub;}idx = end_idx;} else if(s[idx] == ']') {end_idx = idx;return ans;} else {// 正常积累的字符ans = ans + s[idx];}}return ans;}string decodeString(string s) {int end_idx = 0;return solve(s, 0, end_idx);}
};
399. 除法求值
399. 除法求值
题目
给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件,其中 equations[i] = [Ai, Bi] 和 values[i] 共同表示等式 Ai / Bi = values[i] 。每个 Ai 或 Bi 是一个表示单个变量的字符串。
另有一些以数组 queries 表示的问题,其中 queries[j] = [Cj, Dj] 表示第 j 个问题,请你根据已知条件找出 Cj / Dj = ? 的结果作为答案。
返回 所有问题的答案 。如果存在某个无法确定的答案,则用 -1.0 替代这个答案。如果问题中出现了给定的已知条件中没有出现的字符串,也需要用 -1.0 替代这个答案。
注意:输入总是有效的。你可以假设除法运算中不会出现除数为 0 的情况,且不存在任何矛盾的结果。
注意:未在等式列表中出现的变量是未定义的,因此无法确定它们的答案。
含权重的并查集,把之间的权重处理好就可以,感觉 Acwing 的食物链比它难(因为同余比除法难搞,啧啧
class Solution {
public:vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {UnionFind unionFind(2 * values.size());// 把原字符的表示方法映射到 0, ...unordered_map<string, int> hashmap(2 * values.size());int id = 0;for (int i = 0; i < values.size(); i++) {string val1 = equations[i][0], val2 = equations[i][1];if (hashmap.count(val1) == 0) hashmap[val1] = id++;if (hashmap.count(val2) == 0) hashmap[val2] = id++;// id1 / id2 = valueunionFind.unite(hashmap[val1], hashmap[val2], values[i]);}vector<double> ans(queries.size());for (int i = 0; i < queries.size(); i++) {string val1 = queries[i][0], val2 = queries[i][1];if (hashmap.count(val1) == 0 || hashmap.count(val2) == 0) ans[i] = -1.0;else ans[i] = unionFind.query(hashmap[val1], hashmap[val2]);}return ans;}class UnionFind {private:vector<int> parent;vector<double> weight;public:UnionFind (int n) : parent(n), weight(n, 1.0) {for (int i = 0; i < n; i++) parent[i] = i;}int root(int x) {if (x != parent[x]) {int tmp = parent[x];parent[x] = root(parent[x]);// 下面这行真的很难理解,会顾虑 weight[tmp] 是不是连到 root 的有效的// 当然是,不然去求 root[tmp] 的时候会更新为 weight[tmp]weight[x] *= weight[tmp];}return parent[x];}void unite(int x, int y, double value) {// 除法有向,这不能瞎搞// x / y = value// 我的设定为 直接父结点是除数(x),权重是 value// ry / y = weight[y],rx / x = weight[x]// 现在是 rx / ryint rx = root(x), ry = root(y);if (rx == ry) return;parent[ry] = rx;weight[ry] = value * weight[x] / weight[y];}double query(int x, int y) {int rx = root(x), ry = root(y);cout << "root[" << x << "]= " << rx << ", root[" << y << "]= " << ry << endl;cout << "wy= " << weight[y] << ", wx= " << weight[x] << endl;if (rx != ry) return -1.0;// root / y = weight[y], root / x = weight[x]// return x / y // => (root / weight[x]) / (root / weight[y]) = weight[y] / weight[x]return weight[y] / weight[x];}};
};
347. 前K个高频元素
347. 前K个高频元素
题目
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
呃呃,特定学了大根堆小根堆(也没有特地嘻嘻,学了个函数啧啧,这题可是自定义的,很难的喂(#`O′)
其实最好的方法是 快排,而且只用排一半(why?自己想快排原理,和取出第k大的数那题一模一样
class Solution {
public:vector<int> topKFrequent(vector<int>& nums, int k) {unordered_map<int, int> num2cnt;for (int i = 0; i < nums.size(); i++) {num2cnt[nums[i]]++;}auto cmp = [](const pair<int,int>& a, const pair<int,int>& b) {return a.second > b.second; // 小根堆};priority_queue<pair<int,int>, vector<pair<int,int>>, decltype(cmp)> pq(cmp);for (auto item : num2cnt) {int num = item.first, cnt = item.second;if (pq.size() < k) pq.emplace(num, cnt);else {if (pq.top().second < cnt) {pq.pop();pq.emplace(num, cnt);}}}vector<int> ans;while (!pq.empty()) {ans.push_back(pq.top().first);pq.pop();}return ans;}
};
337. 打家劫舍III
337. 打家劫舍III
题目
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
先是 dfs 遍历 TLE 了,然后记忆化搜索来剪枝
(附怎么用自定义的数据结构做哈希)
class Solution {
public:struct PairHash {size_t operator()(const pair<TreeNode*, bool>& p) const {return hash<TreeNode*>()(p.first) ^ (hash<bool>()(p.second) << 1);}};unordered_map<pair<TreeNode*, bool>, int, PairHash> store;int dfs(TreeNode* fa, bool cando) {if (fa == nullptr) return 0;if (store.count({fa, cando})) return store[{fa, cando}];int sum1 = 0, sum2 = 0;if (cando) {sum1 += fa->val;sum1 += dfs(fa->left, false);sum1 += dfs(fa->right, false);}sum2 += dfs(fa->left, true);sum2 += dfs(fa->right, true);return store[{fa, cando}] = max(sum1, sum2);}int rob(TreeNode* root) {return dfs(root, true);}
};
Acwing 的笔记,当时没有树结构,都是拿 int 数组来存关系,这会儿变成 树结构,存不了了,改用两个数组分别承担 [0]/[1]
和 Acwing 没有上司的舞会一模一样的
class Solution {
public:unordered_map<TreeNode*, int> p, q;void dfs(TreeNode* fa) {if (fa == nullptr) return;p[fa] = fa->val;dfs(fa->left);dfs(fa->right);p[fa] += (q[fa->left] + q[fa->right]);q[fa] = max(p[fa->left], q[fa->left]) + max(p[fa->right], q[fa->right]);}int rob(TreeNode* root) {dfs(root);return max(p[root], q[root]);}
};
309. 买卖股票的最佳时机含冷冻期
309. 买卖股票的最佳时机含冷冻期
题目
给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
GPT 你是我的手替!!! 官解的状态及其精练,我以为我不行的www,原来状态模拟也不是太难
class Solution {
public:int maxProfit(vector<int>& prices) {if (prices.empty()) return 0;int n = prices.size();// 状态定义:// f[i][op][hold]// i = 天数// op = 0: 无操作, 1: 买, 2: 卖// hold = 0: 不持有, 1: 持有// hold状态是当天操作完后的vector<vector<vector<int>>> f(n, vector<vector<int>>(3, vector<int>(2, -1e9)));// base casef[0][0][0] = 0; // 第一天无操作 & 不持有f[0][1][1] = -prices[0]; // 第一天买 & 持有for (int i = 1; i < n; i++) {// 1. 今天无操作f[i][0][0] = max(f[i-1][0][0], f[i-1][2][0]); // 前一天不持有,继续不操作f[i][0][1] = max(f[i-1][0][1], f[i-1][1][1]); // 前一天持有,继续持有// 2. 今天买f[i][1][1] = f[i-1][0][0] - prices[i]; // 只能从不持有状态买// 3. 今天卖f[i][2][0] = max(f[i-1][0][1] + prices[i], f[i-1][1][1] + prices[i]); // 卖掉必须持有}// 最后一天不持有股票的最大收益return max(f[n-1][0][0], f[n-1][2][0]);}
};
300. 最长递增子序列
300. 最长递增子序列
题目
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
法一:朴素DP(我竟然想不出)
法二:维护数组尽可能小的数组 + 二分(太熟了,我直接全文默写 www)
class Solution {
public:int lengthOfLIS(vector<int>& nums) {vector<int> store;store.push_back(nums[0]);for (int i = 1; i < nums.size(); i++) {auto it = lower_bound(store.begin(), store.end(), nums[i]);if (it == store.end()) store.push_back(nums[i]);else {int idx = it - store.begin();store[idx] = nums[i];}}return store.size();}
};
287. 寻找重复数
287. 寻找重复数
题目
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
这题很小巧思
被二分查找的名号唬住了,其实也就是二分,背 y总 的板子是关键
class Solution {
public:int findDuplicate(vector<int>& nums) {int n = nums.size();// [1, n - 1]int l = 1, r = n - 1;while (l < r) {int mid = (l + r) >> 1, cnt = 0;for (int i = 0; i < n; i++) {if (mid >= nums[i]) cnt++;}if (cnt > mid) {r = mid;} else {l = mid + 1;}}return l;}
};
279. 完全平方数
279. 完全平方数
题目
给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
有意思噢,大体思路是对的,但是自己的二分弄巧成拙了,不一定是越大的越好,可能为了凑前面的反而更费心呢
class Solution {
public:vector<int> store;int numSquares(int n) {int tmp = 1;while (tmp * tmp <= n) {store.push_back(tmp * tmp);tmp++;}vector<int> f(n + 1, INT_MAX);f[0] = 0;for (int i = 1; i <= n; i++) {for (int sq : store) {if (sq > i) break;f[i] = min(f[i], f[i - sq] + 1);}// int idx = lower_bound(store.begin(), store.end(), i) - store.begin();// for (int j = idx - 1; j >= 0; j--) {// if (f[i - store[j]] != 0) {// f[i] = f[i - store[j]] + 1;// break;// }// }}return f[n];}
};
明天先仔细看一下 GPT 改过的,再看看别的优化思路(18日记,哈哈哈哈优化思路就是有个数学公式 (●’◡’●) )
当我还在抓耳挠腮 4k×(8m+7)4^k \times (8m + 7)4k×(8m+7) 怎么求 k m
bool checkAnswer4(int x) {while (x % 4 == 0) x /= 4;return x % 8 == 7;}
被降维打击了
(突然想到三体,想要是刘慈欣因为这个成语想出第三部也太特喵牛了,一搜,这个词就出自三体…