【周赛笔记】leetcode 周赛笔记
2022.6.19
知识点1:char 转为 string
int i = 1;
char c = 'A' + i;
string ans;
ans += c;
retur ans;
5218 个位数字为K的整数之和
方法一:完全背包
当时一上来就用递归-回溯,结果超出时间限制了。改用完全背包。浪费了不少时间。
以后只有看到是需要记录路径的才用递归回溯,类似最大最小等最优数值优先考虑dp。
class Solution {
public:
int minimumNumbers(int num, int k) {
if (num == 0)
return 0;
vector<int> vec;
for (int i=0; i+k<=num; i=i+10) {
if (i+k != 0)
vec.push_back(i+k);
}
vector<int> dp(num+1, INT_MAX-1);
dp[0] = 0;
for (int i=0; i<vec.size(); ++i) {
for (int j=vec[i]; j<=num; ++j) {
dp[j] = min(dp[j], dp[j-vec[i]] + 1);
}
}
return dp[num] == INT_MAX - 1 ? -1 : dp[num];
}
};
方法二:数学
总结:凡是看到以k为个位数的整数都要想到可以拆成b = a*10 + k
sigma(b) = sigma(a) * 10 + n * k = nums
=> nums - n*k >= 0 && (nums - n*k) % == 0
class Solution {
public:
int minimumNumbers(int num, int k) {
if (num == 0) return 0;
for (int i = 1; i <= num; i++) {
int t = num - k * i;
if (t >= 0 && t % 10 == 0) return i;
}
return -1;
}
};
6099. 小于等于 K 的最长二进制子序列
算法思想:贪心。前缀0越多越好,因为不影响数值。因此后向遍历。如果当前元素为0,都计入长度。如果当前元素为1,则要判断加入sum后是否会超过目标值,如果不超过,才计入长度。
值得注意的是,s的长度很大,但是k限定在一个Int里,因此如果左移超过30位无需判断元素为1的情况,况且也会超出INT的范围。
知识点:无需将string 转为 int 再判断,只需通过左移计算数值。
class Solution {
public:
int longestSubsequence(string s, int k) {
// 后向遍历s,当后缀数值大于k时,仅在当前数值为0时才加入统计
int n = s.size(), cnt = 0, sum = 0;
for (int i=n-1, shift = 0; i>=0; --i, ++shift) {
if (s[i] == '0')
++cnt;
else {
if (shift > 30)
continue;
int add = 1 << shift;
if (sum + add <= k) {
++cnt;
sum += add;
}
}
}
return cnt;
}
};
2022.6.12
5259 计算应缴税款总额
这道简单题花了很多时间来做,实在不应该,嗐...
关键是 循环截止判断 income >= preUp
同时注意 i < n。当时写成 income >= curUp
,然后循环体再补上最后的税收值,导致错误。
class Solution {
public:
double calculateTax(vector<vector<int>>& brackets, int income) {
int n = brackets.size();
double texSum = 0;
int preUp =0, curUp = brackets[0][0], i=0;
while (i<n && income >= preUp) {
texSum += (min(income, curUp) - preUp) * brackets[i][1] * 0.01;
++i;
preUp = curUp;
if (i < n)
curUp = brackets[i][0];
}
return texSum;
}
};
5270. 网格中的最小路径代价
这道题读题都读了好久,对 moveCost 的理解迟迟不到位,后面才搞懂意思。
原先的想法是暴力递归遍历。 重申:所有暴力递归可以解决的问题都可以用动态规划来解决
class Solution {
public:
int minCost;
int minPathCost(vector<vector<int>>& grid, vector<vector<int>>& moveCost) {
int m = grid.size(), n = grid[0].size(), lastNum, minCost;
vector<vector<int>> dp(m, vector<int>(n, 0));
vector<int> lastNumPath;
for (int j=0; j<n; ++j)
dp[0][j] = grid[0][j];
for (int i=1; i<m; ++i) {
for (int j=0; j<n; ++j) {
minCost = INT_MAX;
for (int k=0; k<n; ++k) {
lastNum = grid[i-1][k];
lastNumPath = moveCost[lastNum];
minCost = min(minCost, dp[i-1][k]+lastNumPath[j]);
}
dp[i][j] = minCost + grid[i][j];
}
}
minCost = INT_MAX;
for (auto data : dp[m-1])
minCost = min(minCost, data);
return minCost;
}
};
5289. 公平分发饼干 —— 枚举分配问题
首先观察数据范围:1<=n<=8,显然是用暴力法枚举。
枚举分配问题有2种实现思路:(1)定义篮子数组,用篮子装元素;(2)定义染色数组,先给元素对应的下标染色,然后遍历计算求解想要的数值。
备注:*min_element(arr.begin(), arr.end())
class Solution {
public:
// vector<int> cnt;
int minMaxNum;
int distributeCookies(vector<int>& cookies, int k) {
// cnt.resize(k, 0);
vector<int> cnt(k, 0);
minMaxNum = INT_MAX;
dfs(cookies, cnt, k, 0);
return minMaxNum;
}
void dfs(vector<int>& cookies, vector<int> &cnt, int k, int curIdx) {
int n = cookies.size();
// 全部饼干放到对应篮子后,开始统计
if (curIdx >= n) {
minMaxNum = min(minMaxNum, *max_element(cnt.begin(), cnt.end()));
return;
}
// 将该饼干尝试放到不同的篮子
for (int i=0; i<k; ++i) {
cnt[i] += cookies[curIdx];
dfs(cookies, cnt, k, curIdx+1);
cnt[i] -= cookies[curIdx];
}
}
};
2022.6.5
2296. 设计一个文本编辑器
备注:这道题思路很简单,但实现起来很考验对 STL 代码的掌握。
知识点:
(1)vector::size() 返回的是 unsigned int,如果要与 int 类型进行加减,必须强制转为 int【因为不知道返回值是 unsigned int 浪费了很多时间】
cpp // int n = last.size(); int mvRgtNum = min(k, (int) last.size());
(2)vector::insert(vector::iterator dest, vector::iterator src.begin(), vector::iterator src.end())
prev.insert(prev.end(), text.begin(), text.end());
(3)STL next()函数 与 prev()函数 【虽然本题可以不使用它,但是该方法及其有用】
- next()函数 与 prev()函数 的向前/向后的参数不能越界,否则会出现循环迭代器的问题。
- input_iterator 要么为 BidireactIterator(双向链表迭代器) 要么为 随机访问容器迭代器
例如在 string 的默认构造函数:string( input_iterator start, input_iterator end );
return string(next(left.begin(), max((int) left.size() - 10, 0)), left.end());
代码全解:
方法一:对顶栈法
将 cursor 为界限,分别将前后的子字符串放在不同栈里。由于我们用到 cursor 前 10 个字符,因此我们不用 stack
#include <string>
class TextEditor {
vector<char> prev, last;
public:
TextEditor() {}
void addText(string text) {
prev.insert(prev.end(), text.begin(), text.end());
}
int deleteText(int k) {
// int pn = prev.size();
int dltNum = min(k, (int) prev.size()); // prev.size() 返回的是unsigned int
int cnt = dltNum;
while(cnt-- > 0)
prev.pop_back();
return dltNum;
}
string print() {
int n = prev.size();
int startIdx = max(0, n - 10);
string str;
for (int i=startIdx; i<n; ++i)
str.push_back(prev[i]);
return str;
// return string(next(left.begin(), max((int) left.size() - 10, 0)), left.end());
}
string cursorLeft(int k) {
int mvLftNum = min(k, (int) prev.size());
while (mvLftNum-- > 0) {
last.emplace_back(prev.back());
prev.pop_back();
}
return print();
}
string cursorRight(int k) {
// int n = last.size();
int mvRgtNum = min(k, (int) last.size());
while (mvRgtNum-- > 0) {
prev.emplace_back(last.back());
last.pop_back();
}
return print();
}
};
方法二:链表法
#include <string>
class TextEditor {
private:
list<char> textList;
list<char>::iterator curIt;
public:
TextEditor() {
curIt = textList.begin();
}
void addText(string text) {
for (auto data : text)
textList.insert(curIt, data);
}
int deleteText(int k) {
int cnt = 0;
for (; k && curIt != textList.begin(); --k) {
textList.erase(prev(curIt));
++cnt;
}
return cnt;
}
string print() {
string str;
auto it = curIt;
for (int k = 10; k && it != textList.begin(); --k) {
it = prev(it);
str.push_back(*it);
}
reverse(str.begin(), str.end());
return str;
}
string cursorLeft(int k) {
for (; k && curIt != textList.begin(); --k)
curIt = prev(curIt);
return print();
}
string cursorRight(int k) {
for (; k && curIt != textList.end(); --k)
curIt = next(curIt);
return print();
}
};
2022.5.15
2273. 移除字母异位词后的结果数组
这道题当时没 AC 原因是删除的元素删错了。题目要求是:删除后一个元素,保留前者,我当时是相反。
if (isYiWei(prevStr, curStr))
curIter = prev(words.erase(curIter));
完整代码:
class Solution {
public:
vector<string> removeAnagrams(vector<string>& words) {
int n = words.size();
vector<string>::iterator curIter = words.begin() + n - 1;
vector<string>::iterator prevIter;
string curStr, prevStr;
while (curIter != words.begin()) {
prevIter = prev(curIter);
curStr = *curIter;
prevStr = *prevIter;
if (isYiWei(prevStr, curStr))
curIter = prev(words.erase(curIter));
else
curIter = prevIter;
}
return words;
}
private:
bool isYiWei(string &prevStr, string &curStr) {
if (prevStr.size() != curStr.size())
return false;
vector<int> charMapNum(30, 0);
for (auto data : prevStr)
++charMapNum[data - 'a'];
for (auto data : curStr)
--charMapNum[data - 'a'];
for (auto data : charMapNum) {
if (data != 0)
return false;
}
return true;
}
};
2275. 按位与结果大于零的最长组合
算法思想:组合内的元素按位与结果大于 0,说明必定在某位上这些元素的数值都为 1。题目所给数据范围不超过 2^24次方,设置大小长度为 24 的数组,累加组合内元素的数值为 1 情况。累加和最大的即为结果。
class Solution {
public:
int largestCombination(vector<int>& candidates) {
vector<int> vec(24, 0);
for (auto data : candidates) {
for(int idx=0; data!=0 ; ++idx) {
if (data & 1)
vec[idx] += 1;
data >>= 1;
}
}
int maxN = 0;
for (auto data : vec)
maxN = max(maxN, data);
return maxN;
}
};
2022-05-14
2271 毯子覆盖的最多白色砖块数
双指针法 —— 滑动窗口
要覆盖最多的瓷砖,毯子必定是从数组的左端开始。使用滑动窗口的方式使得 [tiles[lft], tiles[rgt]) 都处于毯子覆盖范围内。左指针 lft 遍历所有数组,右指针 rgt 的位置无需重置,根据毯子的新位置,向前伸长。
本题重点是如何计算 [tiles[lft], tiles[rgt]) 瓷砖的个数 。如果我们是先移动右指针 rgt 再计算,必定超过时间复杂度。为了降低时间复杂度,应该在向前伸长 rgt 的时候进行统计。被覆盖的瓷砖个数由 2 部分组成,一部分为整个数组都被毯子覆盖的瓷砖,一部分为数组的部分成分被毯子覆盖的瓷砖(称为 extra,这部分数值可能为 0)。
注意:只有在比较 maxLen 与 curLen 大小的时候,才需要将 extra 与 curLen相加 max(maxLen, curLen+extra)
,不可将extra叠加到curLen,否则在窗口向右移动(即lft+1)过程会造成重复计算。【这个坑花了我好多时间】
备注:由于右指针右移过程可能会越界,因此需要
while (rgt < n && tiles[rgt][1] <= carpetRgt)
,注意如果while (tiles[rgt][1] <= carpetRgt && rgt < n ) 依然会越界【这个坑花了我些许时间】
class Solution {
public:
int maximumWhiteTiles(vector<vector<int>>& tiles, int carpetLen) {
sort(tiles.begin(), tiles.end());
int n = tiles.size(), lft, rgt = 0, maxLen = 0, curLen = 0;
int carpetRgt;
for (lft=0; lft<n; ++lft) {
if (lft > 0)
curLen -= tiles[lft-1][1] - tiles[lft-1][0] + 1;
carpetRgt = tiles[lft][0] + carpetLen - 1;
// 右指针直至毯子不能完全覆盖
while (rgt < n && tiles[rgt][1] <= carpetRgt) {
curLen += tiles[rgt][1] - tiles[rgt][0] + 1;
++rgt;
}
if (rgt >= n) {
maxLen = max(curLen, maxLen);
return maxLen;
}
int extra = max(0, carpetRgt - tiles[rgt][0] + 1);
maxLen = max(maxLen, curLen + extra);
}
return maxLen;
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端