leetcode 第 389 场周赛
100248. 字符串及其反转中是否存在同一子字符串
简单题 模拟即可
class Solution {
public:
bool isSubstringPresent(string s) {
string rs = s;
reverse(rs.begin(), rs.end());
int n = s.length();
for (int i = 0; i < n - 1; i++) {
string t = s.substr(i, 2);
if (rs.find(t) != string::npos) {
return true;
}
}
return false;
}
};
100236. 统计以给定字符开头和结尾的子字符串总数
中等题 模拟几次即可发现规律
class Solution {
public:
long long countSubstrings(string s, char c) {
int n = s.length(), cnt = 0;
for (int i = 0; i < n; i++) {
if (s[i] == c) cnt++;
}
return 1ll * cnt * (cnt + 1) / 2;
}
};
100255. 成为 K 特殊字符串需要删除的最少字符数
中等题
思路:
- 记录每个字母出现的个数
- 枚举26个字母作为出现个数最少得字母,假设为出现个数为x
- 对于出现个数大于x的字母,需要删除 mp[j] - cnt - k 个
- 对于出现个数小于x的字母,需要删除 mp[j] 个
class Solution {
public:
int minimumDeletions(string word, int k) {
int n = word.length();
vector<int> mp(26, 0);
for (int i = 0; i < n; i++) {
++mp[word[i] - 'a'];
}
int ans = n + 1;
for (int i = 0; i < 26; i++) {
int cnt = mp[i], res = 0;
for (int j = 0; j < 26; j++) {
int temp = mp[j] - cnt;
if (temp >= k) res += mp[j] - cnt - k;
if (temp < 0) res += mp[j];
}
ans = min(ans, res);
}
return ans;
}
};
100227. 拾起 K 个 1 需要的最少行动次数
困难题
参考题解
其余思路见注释
class Solution {
public:
long long minimumMoves(vector<int> &nums, int k, int maxChanges) {
int n = nums.size();
// count[i]: [1, i] 中 1 的个数 (索引从1开始)
vector<int> count(n + 1, 0);
// sum[i]: [1, i] 中 值为 1 的元素的索引之和 (索引从1开始)
vector<long long> sum(n + 1, 0);
for (int i = 1; i <= n; i++) {
count[i] = count[i - 1] + nums[i - 1];
sum[i] = sum[i - 1] + i * nums[i - 1];
}
// 初始化ans
long long ans = LLONG_MAX;
for (int index = 1; index <= n; index++) {
// 还需拾取的 1 的个数
int need = k;
// 当前已行动次数
long long res = 0;
// 拾取 index 上的 1
if (nums[index - 1] == 1) --need;
// 拾取 index-1 上的1
if (need && index - 2 >= 0 && nums[index - 2] == 1) --need, ++res;
// 拾取 index+1 上的1
if (need && index < n && nums[index] == 1) --need, ++res;
// 执行尽可能多个 动作1 + 动作2 的组合
res += min(need, maxChanges) * 2;
need -= min(need, maxChanges);
// 如果已经不需要再拾取了,更新答案,跳过本次循环
if (!need) {
ans = min(ans, res);
continue;
}
// 检查 [index - x, index - 2] 和 [index + 2, index + x] 是否有足够的 1 可供拾取
function<bool(int)> check = [&](int x) -> bool {
int l1 = max(1, index - x), r1 = max(0, index - 2);
int l2 = min(n + 1, index + 2), r2 = min(n, index + x);
// cnt1 =>[index - x, index - 2] 中 1 的个数
// cnt2 =>[index + 2, index + x] 中 1 的个数
int cnt1 = count[r1] - count[l1 - 1], cnt2 = count[r2] - count[l2 - 1];
if (cnt1 + cnt2 < need) return false;
/*
* sum1:左区间 [index - x, index - 2] 中的 1 到 index 距离之和
* 公式推导
* [l1, r1]的点到 index 的距离之和为
* [index - id(l1)] + [index - id(l1 + 1)] + ... + [index - id(r1)]
* = (count[r1] - count[l1-1]) * index - [id(l1) + id(l1 + 1) + ... + id(r1)]
* = cnt1 * index - (sum[r1] - sum[l1-1])
*/
long long sum1 = 1LL * cnt1 * index - (sum[r1] - sum[l1 - 1]);
/*
* sum2:右区间 [index + 2, index + x] 中的 1 到 index 距离之和
* 公式推导
* [l2, r2]的点到 index 的距离之和为
* [id(l2) - index] + [id(l2 + 1) - index] + ... + [id(r2) - index]
* = [id(l2) + id(l2 + 1) + ... + id(r2)] - [index * (count[r2] - count[l2-1])]
* = (sum[r2] - sum[l2-1]) - index * cnt2
*/
long long sum2 = (sum[r2] - sum[l2 - 1]) - 1LL * cnt2 * index;
ans = min(ans, res + sum1 + sum2);
return true;
};
// 二分找到最小的 x,使得仅需考虑区间 [index - x, index + x],就可以拾取到足够的1
// l=2的原因是在上面l=0(即区间[index, index],起点本身),和 l=1(即区间[index-1, index+1])的情况已考虑
int l = 2, r = max(n - index, index - 1);
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) r = mid - 1;
else l = mid + 1;
}
}
return ans;
}
};