Leetcode 第 380 场周赛题解

Leetcode 第 380 场周赛题解

题目1:3005. 最大频率元素计数

思路

遍历数组 nums,统计各元素 num 的出现次数,存储在哈希表 cnt 中。

初始化 sum = 0,max_freq = 0。

遍历哈希表 cnt:

  1. 如果当前 freq > max_freq,则令 max_freq = freq,sum 归 0 后重新加一个 freq(sum = freq)。
  2. 如果当前 freq = max_freq,则 sum += freq。

答案为 sum。

代码

/*
 * @lc app=leetcode.cn id=3005 lang=cpp
 *
 * [3005] 最大频率元素计数
 */

// @lc code=start
class Solution
{
public:
    int maxFrequencyElements(vector<int> &nums)
    {
        // 特判
        if (nums.empty())
            return 0;

        unordered_map<int, int> cnt;
        for (int &num : nums)
            cnt[num]++;

        int max_freq = 0, sum = 0;
        for (auto &[num, freq] : cnt)
        {
            if (freq > max_freq)
            {
                max_freq = freq;
                sum = freq;
            }
            else if (freq == max_freq)
                sum += freq;
        }
        return sum;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(n),其中 n 是数组 nums 的长度。

空间复杂度:O(n),其中 n 是数组 nums 的长度。

题目2:3006. 找出数组中的美丽下标 I

思路

用 C++ 标准库中的字符串查找函数,找到 a 和 b 分别在 s 中的起始下标,将符合要求的下标插入答案。

代码

/*
 * @lc app=leetcode.cn id=3006 lang=cpp
 *
 * [3006] 找出数组中的美丽下标 I
 */

// @lc code=start
class Solution
{
public:
    vector<int> beautifulIndices(string s, string a, string b, int k)
    {
        // 特判
        if (s.empty() || a.empty() || b.empty() || k <= 0 || k > s.size())
            return {};
        if (s.length() < a.length() || s.length() < b.length())
            return {};

        int slen = s.size(), alen = a.size(), blen = b.size();
        vector<int> indices;
        for (int i = 0; i <= slen - alen; i++)
        {
            if (s.substr(i, alen) == a)
            {
                // 注意减小 j 的取值范围
                int start = max(0, i - k), end = min(slen, i + k);
                for (int j = start; j <= end; j++)
                    if (s.substr(j, blen) == b)
                    {
                        indices.push_back(i);
                        break;
                    }
            }
        }
        return indices;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(slen * alen * blen),其中 slen 是字符串 s 的长度,alen 是字符串 a 的长度,blen 是字符串 b 的长度。

空间复杂度:O(1)。

题目3:3007. 价值和小于等于 K 的最大数字

思路

二分答案 + 数位 DP。

题解:三种方法:二分答案+数位 DP/数学公式/逐位构造(Python/Java/C++/Go)

代码

/*
 * @lc app=leetcode.cn id=3007 lang=cpp
 *
 * [3007] 价值和小于等于 K 的最大数字
 */

// @lc code=start

// 二分查找 + 数位 DP

class Solution
{
public:
    long long findMaximumNumber(long long k, int x)
    {
        long long left = 0, right = (k + 1) << x;
        while (left + 1 < right)
        {
            long long mid = left + (right - left) / 2;
            if (countDigitOne(mid, x) <= k)
                left = mid;
            else
                right = mid;
        }
        return left;
    }
    // 辅函数 - 计算所有小于等于 num 的非负整数中的价值总和
    // 一个整数 num 的价值是满足 i % x == 0 且 s[i] 是 1 的 i 的数目
    long long countDigitOne(long long num, int x)
    {
        int m = 64 - __builtin_clzll(num);
        vector<vector<long long>> memo(m, vector<long long>(m + 1, -1));
        function<long long(int, int, bool)> dfs = [&](int i, int cnt1, bool is_limit) -> long long
        {
            if (i < 0)
                return cnt1;
            if (!is_limit && memo[i][cnt1] >= 0)
                return memo[i][cnt1];
            int up = is_limit ? num >> i & 1 : 1;
            long long res = 0;
            for (int d = 0; d <= up; d++) // 枚举要填入的数字 d
                res += dfs(i - 1, cnt1 + (d == 1 && (i + 1) % x == 0), is_limit && d == up);
            if (!is_limit)
                memo[i][cnt1] = res;
            return res;
        };
        return dfs(m - 1, 0, true);
    }
};
// @lc code=end

复杂度分析

在这里插入图片描述

题目4:3008. 找出数组中的美丽下标 II

思路

KMP + 二分查找。

KMP 详解:KMP 算法详解(C++ Version)

题解:两种写法:KMP+二分查找/双指针(Python/Java/C++/Go)

代码

/*
 * @lc app=leetcode.cn id=3008 lang=cpp
 *
 * [3008] 找出数组中的美丽下标 II
 */

// @lc code=start

// KMP + 二分查找

class Solution
{
public:
    vector<int> beautifulIndices(string s, string a, string b, int k)
    {
        vector<int> posA = kmp(s, a);
        vector<int> posB = kmp(s, b);

        vector<int> ans;

        // 二分查找
        // for (int i : posA)
        // {
        //     auto it = lower_bound(posB.begin(), posB.end(), i);
        //     if (it != posB.end() && *it - i <= k ||
        //         it != posB.begin() && i - *--it <= k)
        //     {
        //         ans.push_back(i);
        //     }
        // }

        // 双指针
        int j = 0, m = posB.size();
        for (int i : posA)
        {
            while (j < m && posB[j] < i - k)
                j++;
            if (j < m && posB[j] <= i + k)
                ans.push_back(i);
        }

        return ans;
    }
    // 辅函数 - KMP
    vector<int> kmp(string &text, string &pattern)
    {
        int m = pattern.length();
        vector<int> nxt = getNxt(pattern);
        vector<int> res;
        int tar = 0; // 主串中将要匹配的位置
        int pos = 0; // 模式串中将要匹配的位置
        while (tar < text.length())
        {
            if (text[tar] == pattern[pos])
            {
                // 若两个字符相等,则 tar、pos 各进一步
                tar++;
                pos++;
            }
            else if (pos != 0)
            {
                // 失配,如果 pos != 0,则依据 nxt 移动标尺
                pos = nxt[pos - 1];
            }
            else
            {
                // pos[0] 失配,标尺右移一位
                tar++;
            }

            // pos 走到了 pattern.length(),匹配成功
            if (pos == pattern.length())
            {
                // 保存主串上的匹配起点
                res.push_back(tar - pos);
                // 移动标尺
                pos = nxt[pos - 1];
            }
        }
        return res;
    }
    // 辅函数 - 计算 nxt 数组
    // vector<int> getNxt(string &pattern)
    // {
    //     int m = pattern.length();
    //     vector<int> nxt(m, 0);
    //     for (int i = 0; i < m; i++)
    //     {
    //         string sub = pattern.substr(0, i + 1);
    //         for (int j = sub.length() - 1; j >= 0; j--)
    //         {
    //             string prev = sub.substr(0, j);
    //             string suf = sub.substr(sub.length() - j, j);
    //             if (prev == suf)
    //             {
    //                 nxt[i] = j;
    //                 break;
    //             }
    //         }
    //     }
    //     return nxt;
    // }
    vector<int> getNxt(string &pattern)
    {
        vector<int> nxt;
        // next[0] 必然是 0
        nxt.push_back(0);
        // 从 next[1] 开始求
        int x = 1, now = 0;
        while (x < pattern.length())
        {
            if (pattern[now] == pattern[x])
            {
                // 如果 pattern[now] == pattern[x],向右拓展一位
                now++;
                x++;
                nxt.push_back(now);
            }
            else if (now != 0)
            {
                // 缩小 now,改成 nxt[now - 1]
                now = nxt[now - 1];
            }
            else
            {
                // now 已经为 0,无法再缩小,故 next[x] = 0
                nxt.push_back(0);
                x++;
            }
        }
        return nxt;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(nlog⁡n),其中 n 为字符串 text 的长度。

空间复杂度:O(n)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值