mirror of
https://gitlab.com/game-loader/hugo.git
synced 2025-05-04 22:32:06 +08:00
leetcode update
This commit is contained in:
parent
36ebe60b0c
commit
763fc4b56f
@ -23022,3 +23022,129 @@ public:
|
||||
```
|
||||
### 总结
|
||||
注意到想构建优先级队列需要定义好三个属性,一是优先级队列中保存的数据类型,二是用来保存这些数据的容器类型,三则是优先级队列中的比较函数,定义好这三个抽象出来的属性就可以定义好一个优先级队列。
|
||||
|
||||
## day341 2025-02-14
|
||||
### 1352. Product of the Last K Numbers
|
||||
Design an algorithm that accepts a stream of integers and retrieves the product of the last k integers of the stream.
|
||||
|
||||
Implement the ProductOfNumbers class:
|
||||
|
||||
ProductOfNumbers() Initializes the object with an empty stream.
|
||||
void add(int num) Appends the integer num to the stream.
|
||||
int getProduct(int k) Returns the product of the last k numbers in the current list. You can assume that always the current list has at least k numbers.
|
||||
The test cases are generated so that, at any time, the product of any contiguous sequence of numbers will fit into a single 32-bit integer without overflowing.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题注意要求添加和获取尾部乘积的时间复杂度都为O(1),添加数字比较简单,直接向数组尾部添加数字就能满足时间复杂度,计算乘积相对来说复杂一些,因为添加数字是从前向后添加,此处考虑如果是获取尾部的k个数字的和,首先想到的就是使用前缀和,用n对应的前缀和和n-k对应的前缀和做差即得尾部的k个数字的和,前缀和的好处在于从前向后添加数字的过程中可以一边添加数字一边计算出来且计算出来后不会发生变化,后续不需要额外的计算,但后缀和在向尾部添加数字的时前面数字的后缀和一直在改变。
|
||||
|
||||
本题也可以采用类似的思路,只是使用的不是前缀和而是前缀积,要获取尾部k个数字的积只需将n对应的前缀积和n-k对应的前缀积做除法。但乘积有一个特殊的因素,即出现0的情况,在出现0后对应的前缀积直接变为0,假设0出现在位置m处,则全部包含m位置的积都为0,此处常规状态下做除的方式不再适用,因此对于前缀积,当出现0时就将整个前缀积数组清空,后续计算尾部k个数字的积时如果k大于当前前缀积数组的长度,说明这k个数字中一定包含一个0,直接返回0即可(初始状态除外即还没有遇到过0,但数组长度小于k的情况)。其余情况下使用前缀积做除可得尾部k个数字的积且满足时间复杂度为O(1)
|
||||
|
||||
此处在每次重置前缀积数组时先放置一个哨兵数字1,就可以使用同一种处理方式直接处理数组长度大于等于k的情况(等于k时不用再额外处理,可以通用做除的方式)。
|
||||
|
||||
### 代码
|
||||
```cpp
|
||||
class ProductOfNumbers {
|
||||
public:
|
||||
vector<int> nums;
|
||||
vector<int> products;
|
||||
ProductOfNumbers() {
|
||||
products.push_back(1);
|
||||
}
|
||||
|
||||
void add(int num) {
|
||||
nums.push_back(num);
|
||||
if(num == 0){
|
||||
products.clear();
|
||||
products.push_back(1);
|
||||
}else{
|
||||
products.push_back(products[products.size()-1]*num);
|
||||
}
|
||||
}
|
||||
|
||||
int getProduct(int k) {
|
||||
if(products.size() <= k){
|
||||
return 0;
|
||||
}else{
|
||||
return products[products.size()-1]/products[products.size()-1-k];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Your ProductOfNumbers object will be instantiated and called as such:
|
||||
* ProductOfNumbers* obj = new ProductOfNumbers();
|
||||
* obj->add(num);
|
||||
* int param_2 = obj->getProduct(k);
|
||||
*/
|
||||
```
|
||||
## day342 2025-02-15
|
||||
### 2698. Find the Punishment Number of an Integer
|
||||
Given a positive integer n, return the punishment number of n.
|
||||
|
||||
The punishment number of n is defined as the sum of the squares of all integers i such that:
|
||||
|
||||
1 <= i <= n
|
||||
The decimal representation of i * i can be partitioned into contiguous substrings such that the sum of the integer values of these substrings equals i.
|
||||
|
||||

|
||||
|
||||
### 题解
|
||||
本题注意题目条件,n的范围仅有1000,而惩罚数计算方式中整数i的条件的第二条,i的平方可以被分割成两个子字符串且子字符串代表的整数的和与i相等这一条件只和i本身有关,也就是说i是否满足该条件可以提前算出,因此可以直接遍历1~1000,依次计算i是否满足该条件,若满足则将i的平方加入到前缀和中,用数组保存到下标i的满足条件的所有数字的平方的前缀和。对于n,直接使用查表法查到n对应的平方前缀和即为最终结果。
|
||||
|
||||
要解决的关键问题即为如何判断i是否满足第二个条件,类似的问题之前也曾多次遇到,对于该问题只能遍历出字符串的所有可能的分割组合并判断,只是在遍历过程中可以通过条件进行剪枝。遍历字符串分割组合可以使用回溯法,即先分割前面一个字符,再递归处理后面的字符串,再分割前面两个字符...以此类推。在处理过程中可以传入i减去之前分割出来的数字后的差,即剩余的还需要填补的数量,如果差为负数说明前面的数字已经过大,可以直接从该分支中返回继续下一个分支的分割。
|
||||
|
||||
此时可以想到,将数字和字符串来回转化要花费大量的时间,是否可以避免这种转换开销呢,对于字符串,我们每次可以从前向后先分割出一个字符,再分割出两个字符,而对于数字,其实也可以做类似的操作,只是会从右向左,如果将数字模10,就可以分割出一个最右侧数字,将数字模100,就可以分割出两个最右侧数字,由于本题中我们找计算的是分割的组合,因此分割的方向不重要,这种分割方法得到的结果是相同的但避免了转换开销。
|
||||
|
||||
### 代码
|
||||
```cpp
|
||||
class Solution {
|
||||
public:
|
||||
int punishmentNumber(int n) {
|
||||
std::vector<int> prefix_sums(1001, 0);
|
||||
for (int i = 1; i <= 1000; ++i) {
|
||||
prefix_sums[i] = prefix_sums[i - 1];
|
||||
if (is_punishment_number(i)) {
|
||||
prefix_sums[i] += i * i;
|
||||
}
|
||||
}
|
||||
return prefix_sums[n];
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_punishment_number(int i) {
|
||||
return can_partition(i * i, i);
|
||||
}
|
||||
|
||||
bool can_partition(int num, int remaining_target) {
|
||||
if (num == 0) {
|
||||
return remaining_target == 0;
|
||||
}
|
||||
|
||||
int divisor = 1;
|
||||
|
||||
while (num / divisor > 0) {
|
||||
int current_num = num % (divisor*10);
|
||||
|
||||
if (current_num > remaining_target) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (can_partition(num / (divisor * 10), remaining_target - current_num))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (divisor > num / 10) break;
|
||||
divisor *= 10;
|
||||
|
||||
}
|
||||
if(num == remaining_target){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
```
|
||||
|
Loading…
Reference in New Issue
Block a user