Leetcode周赛378总结

Q1 检查按位或是否存在尾随0

  • 题目链接

  • 解题思路:

    • 检查数组中有多少个奇数
    • 如果有大于等于两个奇数,返回true,否则返回false
  • 解题代码:


class Solution {
public:
    bool hasTrailingZeros(vector<int>& nums) {
        int cnt = 0;
        for (auto num : nums) {
            if (num & 1) {
                continue;
            }
            cnt += 1;
            if (cnt == 2) {
                return true;
            }
        }
        return false;
    }
};

Q2 找出出现至少三次的最长特殊子字符串I

  • 题目链接

  • 解题思路:

    • 数据范围可以暴力
    • 枚举每个子字符串,用哈希表统计各个特殊子字符串的出现次数
  • 解题代码:


class Solution {
public:
    int maximumLength(string s) {
        int n = s.size();
        unordered_map<string, int> um;
        for (int i = 0; i < n; i++) {
            int j = i+1;
            string temp;
            temp.push_back(s[i]);
            um[temp] += 1;
            while (j < n && s[j] == s[j-1]) {
                temp += s[j];
                um[temp] += 1;
                j += 1;
            }
        }  
        int ans = -1;
        for (auto &[s, cnt] : um) {
            int len = s.size();
            if (cnt >= 3 && len > ans) {
                ans = len;
            }
        }
        return ans;
    }
};

Q3 找出出现至少三次的最长特殊子字符串II

  • 题目链接

  • 解题思路:

    • 分别记录字符串中各个字符连续出现的子串长度

    • 枚举每种出现过的字符,分三种情况来讨论:

      • 在最大的长度中,选择该长度-2的特殊子字符串

      • 在最大和次大的长度中:

        • 如果两个长度相等,选择该长度-1的特殊子字符串
        • 如果两个长度不等,选择次大长度的特殊子字符串
      • 在最大、次大和第三大长度中,选择第三大长度的特殊子字符串

    • 答案为上述所有情况中的最大值

  • 解题代码:


class Solution {
public:
    int maximumLength(string s) {
        int n = s.size();
        vector<vector<int>> cnts(26);
        for (int i = 0; i < n; i++) {
            int j = i+1;
            while (j < n && s[j] == s[j-1]) {
                j += 1;
            }
            cnts[s[i]-'a'].push_back(j-i);
            i = j-1;
        }
        int ans = -1;
        for (int i = 0; i < 26; i++) {
            auto &cnt = cnts[i];
            sort(cnt.rbegin(), cnt.rend());
            // 最长的拆三份
            if (cnt.size() > 0 && cnt[0] >= 3) {
                ans = max(ans, cnt[0] - 2);
            }
            if (cnt.size() >= 2) {
                // 如果两个相等
                if (cnt[0] == cnt[1] && cnt[0] != 1) {
                    ans = max(ans, cnt[0] - 1);
                }
                // 长的拆两份,短的不动
                if (cnt[0] > cnt[1]) {
                    ans = max(ans, cnt[1]);
                }
            }
            if (cnt.size() >= 3) {
                ans = max(ans, cnt[2]);
            }
        }
        return ans;
    }
};
   

Q4 回文串重新排列查询

  • 题目链接

  • 解题思路:

    • 不能重新排列的区间,必须满足s[i] = t[i]
    • 可以重新排列的区间,需要判断各字符出现次数是否一致
    • 分情况讨论区间:
      • 包含,直接判断大区间内各字符出现次数是否一致即可
      • 相交但不包含, 分别计算s和t除去不能重新排列位置之外的字符出现次数,比较两个出现次数是否相同
      • 不相交,分别判断各区间情况
  • 解题代码:


class Solution {
public:
    vector<bool> canMakePalindromeQueries(string s, vector<vector<int>>& queries) {
        int n = s.size() / 2;
        string t = s.substr(n, n);
        reverse(t.begin(), t.end());
        s = s.substr(0, n);
        // 预处理前缀和
        vector<vector<int>> sum_s(n+1, vector<int>(26, 0));
        for (int i = 1; i <= n; i++) {
            sum_s[i] = sum_s[i-1];
            sum_s[i][s[i-1]-'a'] += 1;
        }
        vector<vector<int>> sum_t(n+1, vector<int>(26, 0));
        for (int i = 1; i <= n; i++) {
            sum_t[i] = sum_t[i-1];
            sum_t[i][t[i-1]-'a'] += 1;
        }
        vector<int> eq(n+1, 0);
        for (int i = 1; i <= n; i++) {
            eq[i] = eq[i-1] + (s[i-1] == t[i-1]);
        }
        // 一些需要的函数
        // 判断区间内字符出现次数是否相等
        auto check_eq = [&](int l, int r) -> bool {
            for (int i = 0; i < 26; i++) {
                int temp1 = sum_s[r+1][i] - sum_s[l][i];
                int temp2 = sum_t[r+1][i] - sum_t[l][i];
                if (temp1 != temp2) {
                    return false;
                }
            }
            return true;
        };
        // 返回差集
        auto substract = [&](int l1, int r1, int l2, int r2, vector<vector<int>>& sum_s, vector<vector<int>>& sum_t) -> vector<int> {
            vector<int> ans(26, 0);
            for (int i = 0; i < 26; i++) {
                int temp1 = sum_s[r1+1][i] - sum_s[l1][i];
                int temp2 = sum_t[r2+1][i] - sum_t[l2][i];
                if (temp1 < temp2) {
                    return {};
                }
                ans[i] = temp1 - temp2;
            }
            return ans;
        };
        auto func = [&](int l1, int r1, int l2, int r2, vector<vector<int>>& sum_s, vector<vector<int>>& sum_t) -> bool {
            // l1 <= l2 的
            // [0, l1-1], [max(r1, r2) + 1, n-1] 需要完全相同
            if (eq[l1] - eq[0] != l1 || eq[n] - eq[max(r1, r2)+1] != n - 1 -max(r1, r2)) {
                return false;
            }
            if (r1 >= r2) {
                // 包含
                if (!check_eq(l1, r1)) {
                    return false;
                }
            } else if (r1 < l2) {
                // 不相交 [r1+1, l2-1]也是不可重新排列的区间
                if (!check_eq(l1, r1) || !check_eq(l2, r2) || eq[l2] - eq[r1+1] != l2-r1-1) {
                    return false;
                }
            } else {
                // 相交但不包含
                auto s1 = substract(l1, r1, l1, l2-1, sum_s, sum_t); // sum_s[l1, r1] - sum_t[l1, l2-1]
                auto s2 = substract(l2, r2, r1+1, r2, sum_t, sum_s); // sum_t[l2, r2] - sum_s[r1+1, r2]
                if (s1.empty() || s2.empty()) {
                    return false;
                }
                for (int i = 0; i < 26; i++) {
                    if (s1[i] != s2[i]) {
                        return false;
                    }
                }
            }
            return true;
        };
        vector<bool> ans;
        for (auto &query : queries) {
            int l1 = query[0], r1 = query[1], l2 = n * 2 - 1 - query[3], r2 = n * 2 - 1 - query[2];
            if (l2 < l1) {
                ans.push_back(func(l2, r2, l1, r1, sum_t, sum_s));
            } else {
                ans.push_back(func(l1, r1, l2, r2, sum_s, sum_t));
            }
        }
        return ans;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值