Leetcode 第 378 场周赛题解

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=start

class 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。

分类讨论:

  1. 从最长的特殊子串(group[0])中取三个长度均为 group[0]−2 的特殊子串。例如示例 1 的 aaaa 可以取三个 aa。
  2. 从最长和次长的特殊子串(group[0]、group[1])中取三个长度一样的特殊子串:min⁡(group[0]−1, group[1])。
  3. 从最长、次长、第三长的的特殊子串(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。

你可能感兴趣的:(Every,day,a,LeetCode,leetcode,C++,数据结构与算法)