Leetcode 第 380 场周赛题解
Leetcode 第 380 场周赛题解
题目1:3005. 最大频率元素计数
思路
遍历数组 nums,统计各元素 num 的出现次数,存储在哈希表 cnt 中。
初始化 sum = 0,max_freq = 0。
遍历哈希表 cnt:
- 如果当前 freq > max_freq,则令 max_freq = freq,sum 归 0 后重新加一个 freq(sum = freq)。
- 如果当前 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(nlogn),其中 n 为字符串 text 的长度。
空间复杂度:O(n)。