Q1.限制有序数组中的元素出现次数
题目
- 给定升序整数数组
nums和整数k - 返回去重后每个元素最多保留
k个的数组(保持原顺序)
思路
- 模拟
代码
cpp
class Solution {
public:
vector<int> limitOccurrences(vector<int>& nums, int k) {
vector<int> ans;
int cnt = 1;
ans.push_back(nums[0]);
for (int i = 1; i < nums.size(); i++) {
if (nums[i] == nums[i-1])
cnt++;
else
cnt = 1;
if (cnt <= k) {
ans.push_back(nums[i]);
}
}
return ans;
}
};
Q2. 密码强度
🔗 https://leetcode.cn/contest/weekly-contest-503/problems/password-strength/description/
题目
- 给定字符串
password - 按出现过的不同字符计分并求和:小写字母 1分,大写字母 2分,数字 3分,特殊字符 5分
- 返回总分
思路
- 模拟
代码
cpp
class Solution {
public:
int passwordStrength(string password) {
int ans = 0;
set<char> s;
for (char ch : password) {
if (s.count(ch)) continue;
s.insert(ch);
if (ch >= '0' && ch <= '9') ans += 3;
if (ch >= 'a' && ch <= 'z') ans += 1;
if (ch >= 'A' && ch <= 'Z') ans += 2;
if (ch == '!' || ch == '@' || ch == '#' || ch == '$') ans += 5;
}
return ans;
}
};
Q3. 排序排列的最少操作数
题目
- 给定
[0..n-1]的一个排列nums - 每次只能"反转整个数组"或"左旋一位"
- 返回将其排成升序的最少操作次数,无法排序则返回
-1
思路
- 最终都升序,需要当前的梯度变化仅一次
- 即若当前都升序,仅存在一处降序;反之,若当前都降序,仅存在一处升序;都在首位相交的地方;
- 找到升序开始的位置 idx,也就是 0 的位置,调整的方案:
- 左旋 idx 次
- 先反转,再左旋 len - idx 次,再反转
- 找到降序开始的位置 idx,调整的方案:
- 左旋 idx 次,反转
- 反转,左旋 len - idx 次
代码
cpp
class Solution {
public:
int minOperations(vector<int>& nums) {
int len = nums.size();
if (len == 1) return 0;
vector<int> n(nums);
for (auto num : nums) {
n.push_back(num);
}
int asc = 1, dsc = 1;
int max_asc = -1, max_dsc = -1;
for (int i = 1; i < n.size(); i++) {
if (n[i] > n[i-1]) {
asc++;
dsc = 1;
} else {
asc = 1;
dsc++;
}
if (max_asc == -1 && asc == len) max_asc = i;
if (max_dsc == -1 && dsc == len) max_dsc = i;
}
if (max_asc == -1 && max_dsc == -1) return -1;
int ans = n.size();
if (max_dsc != -1) {
int idx = max_dsc - len + 1;
ans = min(ans, idx + 1); // 左旋 idx + 反转
ans = min(ans, 1 + len - idx); // 反转 + 左旋
}
if (max_asc != -1) {
int idx = max_asc - len + 1;
ans = min(ans, idx); // 左旋
ans = min(ans, 2 + len - idx); // 反转 + 左旋 + 反转
}
return ans;
}
};
Q4. 递增后的数对数量
🔗 https://leetcode.cn/contest/weekly-contest-503/problems/number-of-pairs-after-increment/
题目
- 给定数组
nums1、nums2和查询queries - 支持两种操作:
- 区间加(将
nums2[x..y]每个元素加val); - 计数(统计满足
nums1[j] + nums2[k] == tot的下标对(j,k)数量)
- 区间加(将
- 返回每次计数查询的结果数组
思路
- 暴力必定 TLE,nums1 的长度不超过 5,核心就是精简对 num2 上的操作,采用分块法
- 每个块上的 add_val,就记做一个整体,块两边的,逐个更新;
- 块大小的最优解为 sqrt(num2.size()),作为一种平衡
- 注意:map 直接 count 不存在的元素,会插入元素,对性能有影响
代码
cpp
class Solution {
public:
vector<int> numberOfPairs(vector<int>& nums1, vector<int>& nums2, vector<vector<int>>& queries) {
struct Block {
unordered_map<long long, int> freq;
long long lazy_val;
};
// init block with map & lazy_val
int n = nums2.size();
int size = sqrt(n);
int B = (n + size - 1) / size;
vector<long long> arr(n);
vector<Block> block(B);
for (int i = 0;i < n; i++) {
arr[i] = nums2[i];
block[i / size].freq[nums2[i]]++;
block[i / size].lazy_val = 0;
}
// process queryies
vector<int> ans;
auto add = [&](int x, int y, int val) {
int l = x / size;
int r = y / size;
if (l == r) {
for (int i = x; i <= y; i++) {
if (--block[l].freq[arr[i]] == 0) block[l].freq.erase(arr[i]);
arr[i] += val;
block[l].freq[arr[i]]++;
}
return;
}
for (int i = x; i < (l + 1) * size; i++) {
if (--block[l].freq[arr[i]] == 0) block[l].freq.erase(arr[i]);
arr[i] += val;
block[l].freq[arr[i]]++;
}
for (int i = l + 1; i < r; i++) {
block[i].lazy_val += val;
}
for (int i = r * size; i <= y; i++) {
if (--block[r].freq[arr[i]] == 0) block[r].freq.erase(arr[i]);
arr[i] += val;
block[r].freq[arr[i]]++;
}
};
auto count = [&](int tot) {
int cnt = 0;
for (int i = 0; i < B; i++) {
for (int j = 0; j < nums1.size(); j++) {
long long target = tot - nums1[j] - block[i].lazy_val;
auto it = block[i].freq.find(target);
if (it != block[i].freq.end()) {
cnt += it->second;
}
}
}
ans.push_back(cnt);
};
for (auto query : queries) {
if (query[0] == 1) {
add(query[1], query[2], query[3]);
} else {
count(query[1]);
}
}
return ans;
}
};