leetcode 第 389 场周赛

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 特殊字符串需要删除的最少字符数

中等题
思路:

  1. 记录每个字母出现的个数
  2. 枚举26个字母作为出现个数最少得字母,假设为出现个数为x
  3. 对于出现个数大于x的字母,需要删除 mp[j] - cnt - k 个
  4. 对于出现个数小于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;
    }
};
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值