Leetcode 第 378 场周赛题解
- Leetcode 第 378 场周赛题解
- 题目1:2980. 检查按位或是否存在尾随零
- 思路
- 代码
- 复杂度分析
- 题目2:2981. 找出出现至少三次的最长特殊子字符串 I
- 思路
- 代码
- 复杂度分析
- 题目3:2982. 找出出现至少三次的最长特殊子字符串 II
- 思路
- 代码
- 复杂度分析
- 题目4:2983. 回文串重新排列查询
- 思路
- 代码
- 复杂度分析
Leetcode 第 378 场周赛题解
题目1:2980. 检查按位或是否存在尾随零
思路
遍历。
给你一个 正整数数组 nums 。
你需要检查是否可以从数组中选出两个或更多元素,满足这些元素的按位或运算( OR)结果的二进制表示中至少存在一个尾随零(最低位为 0)。
或运算要求两个元素的最低位都为 0,才能使得结果的最低位为 0,即至少存在一个尾随零。
于是问题转换为正整数数组 nums 至少存在 2 个最低位为 0 的元素,即至少存在 2 个偶数。
代码
/** @lc app=leetcode.cn id=2980 lang=cpp** [2980] 检查按位或是否存在尾随零*/// @lc code=start
class Solution
{
public:bool hasTrailingZeros(vector<int> &nums){int count = 0;for (int &num : nums)if (num % 2 == 0)count++;return count >= 2;}
};
// @lc code=end
复杂度分析
时间复杂度:O(n),其中 n 是数组 nums 的长度。
空间复杂度:O(1)。
题目2:2981. 找出出现至少三次的最长特殊子字符串 I
思路
滑动窗口枚举窗口内字符相同的字符串,再暴力枚举长度相等的字符串。
代码
/** @lc app=leetcode.cn id=2981 lang=cpp** [2981] 找出出现至少三次的最长特殊子字符串 I*/// @lc code=startclass Solution
{
public:int maximumLength(string s){// 特判if (s.empty())return 0;int n = s.size();int ans = -1, left = 0;for (int right = 0; right < n; right++){while (s[left] != s[right])left++;int len = right - left + 1;string sub = s.substr(left, len);int count = 1;// 暴力枚举所有的子字符串for (int i = left + 1; i < n; i++){string temp = s.substr(i, len);if (sub == temp){count++;if (count >= 3){ans = max(ans, len);break;}}}}return ans;}
};
// @lc code=end
复杂度分析
时间复杂度:O(n2),其中 n 是字符串 s 的长度。
空间复杂度:O(1)。现至少三次的最长特殊子字符串 II
题目3:2982. 找出出现至少三次的最长特殊子字符串 II
思路
字符串分割 + 分类讨论。
按照相同字母分组,每组统计相同字母连续出现的长度。例如 aaaabbbabb 把 a 分成一组,组内有长度 4 和长度 1;把 b 分成一组,组内有长度 3 和长度 2。
单独考虑每一组,按照长度从大到小排序,设长度列表为 group。
分类讨论:
- 从最长的特殊子串(group[0])中取三个长度均为 group[0]−2 的特殊子串。例如示例 1 的 aaaa 可以取三个 aa。
- 从最长和次长的特殊子串(group[0]、group[1])中取三个长度一样的特殊子串:min(group[0]−1, group[1])。
- 从最长、次长、第三长的的特殊子串(group[0]、group[1]、group[2])中各取一个长为 group[2]的特殊子串。
这三种情况取最大值,即:max({group[0] - 2, min(group[0] - 1, group[1]), group[2]})。取每一组的最大值,即为答案。
如果答案是 0,返回 −1。
代码实现时,无需特判 group 数组长度小于 3 的情况,我们只需要在数组添加两个空串(在数组末尾加两个 0)即可。
代码
/** @lc app=leetcode.cn id=2981 lang=cpp** [2981] 找出出现至少三次的最长特殊子字符串 I*/// @lc code=start
class Solution
{
public:int maximumLength(string s){// 特判if (s.empty())return 0;vector<int> groups[26];int n = s.length();int count = 0;for (int i = 0; i < n; i++){count++;if (i + 1 == n || s[i] != s[i + 1]){groups[s[i] - 'a'].push_back(count); // 统计连续字符长度count = 0;}}int ans = 0;for (vector<int> &group : groups){if (group.empty())continue;// 降序排序sort(group.begin(), group.end(), greater<int>());// 假设还有两个空串group.push_back(0);group.push_back(0);ans = max({ans, group[0] - 2, min(group[0] - 1, group[1]), group[2]});}return ans == 0 ? -1 : ans;}
};
// @lc code=end
复杂度分析
时间复杂度:O(nlogn),其中 n 是字符串 s 的长度。
空间复杂度:O(n),其中 n 是字符串 s 的长度。
题目4:2983. 回文串重新排列查询
思路
大模拟。
题解:分类讨论(Python/Java/C++/Go)
代码
/** @lc app=leetcode.cn id=2983 lang=cpp** [2983] 回文串重新排列查询*/// @lc code=start
class Solution
{
public:vector<bool> canMakePalindromeQueries(string s, vector<vector<int>> &queries){// 分成左右两半,右半反转int n = s.length() / 2;string t = s.substr(n);reverse(t.begin(), t.end());// 预处理三种前缀和vector<vector<int>> sum_s(n + 1, vector<int>(26));for (int i = 0; i < n; i++){sum_s[i + 1] = sum_s[i];sum_s[i + 1][s[i] - 'a']++;}vector<vector<int>> sum_t(n + 1, vector<int>(26));for (int i = 0; i < n; i++){sum_t[i + 1] = sum_t[i];sum_t[i + 1][t[i] - 'a']++;}vector<int> sum_ne(n + 1);for (int i = 0; i < n; i++){sum_ne[i + 1] = sum_ne[i] + (s[i] != t[i]);}// 计算子串中各个字符的出现次数,闭区间 [l, r]auto count = [](vector<vector<int>> &sum, int l, int r) -> vector<int>{auto res = sum[r + 1];for (int i = 0; i < 26; i++){res[i] -= sum[l][i];}return res;};auto subtract = [](vector<int> s1, vector<int> s2) -> vector<int>{for (int i = 0; i < 26; i++){s1[i] -= s2[i];if (s1[i] < 0){return {};}}return s1;};auto check = [&](int l1, int r1, int l2, int r2, vector<vector<int>> &sumS, vector<vector<int>> &sumT) -> bool{if (sum_ne[l1] > 0 || // [0, l1-1] 有 s[i] != t[i]sum_ne[n] - sum_ne[max(r1, r2) + 1] > 0){ // [max(r1,r2)+1, n-1] 有 s[i] != t[i]return false;}// 区间包含if (r2 <= r1){return count(sumS, l1, r1) == count(sumT, l1, r1);}// 区间不相交if (r1 < l2){return sum_ne[l2] - sum_ne[r1 + 1] == 0 && // [r1+1, l2-1] 都满足 s[i] == t[i]count(sumS, l1, r1) == count(sumT, l1, r1) &&count(sumS, l2, r2) == count(sumT, l2, r2);}// 区间相交但不包含auto s1 = subtract(count(sumS, l1, r1), count(sumT, l1, l2 - 1));auto s2 = subtract(count(sumT, l2, r2), count(sumS, r1 + 1, r2));return !s1.empty() && !s2.empty() && s1 == s2;};vector<bool> ans(queries.size());for (int i = 0; i < queries.size(); i++){auto &q = queries[i];int l1 = q[0], r1 = q[1], l2 = n * 2 - 1 - q[3], r2 = n * 2 - 1 - q[2];ans[i] = l1 <= l2 ? check(l1, r1, l2, r2, sum_s, sum_t) : check(l2, r2, l1, r1, sum_t, sum_s);}return ans;}
};
// @lc code=end
复杂度分析
时间复杂度:O((n+q)∣Σ∣),其中 n 为字符串 s 的长度,q 为数组 queries 的长度,∣Σ∣ 为字符集合的大小,本题中字符均为小写字母,所以 ∣Σ∣=26。回答每个询问的时间是 O(∣Σ∣)。
空间复杂度:O(n∣Σ∣),其中 n 为字符串 s 的长度,∣Σ∣ 为字符集合的大小,本题中字符均为小写字母,所以 ∣Σ∣=26。