【力扣周赛】第 112 场双周赛(统计一个字符串的 k 子序列美丽值最大的数目(贪心+计数+组合数学)

竞赛链接

https://leetcode.cn/contest/biweekly-contest-112/

Q1:7021. 判断通过操作能否让字符串相等 I

https://leetcode.cn/problems/check-if-strings-can-be-made-equal-with-operations-i/

在这里插入图片描述

提示:
s1.length == s2.length == 4
s1 和 s2 只包含小写英文字母。

class Solution {
    public boolean canBeEqual(String s1, String s2) {
        // 取出各个字符
        char a = s1.charAt(0), b = s1.charAt(1), c = s1.charAt(2), d = s1.charAt(3);
        char a2 = s2.charAt(0), b2 = s2.charAt(1), c2 = s2.charAt(2), d2 = s2.charAt(3);
        // 比较
        return (a == a2 || a == c2) && (a + c == a2 + c2) && (b == b2 || b == d2) && (b + d == b2 + d2);
    }
}

Q2:7005. 判断通过操作能否让字符串相等 II(贪心)

https://leetcode.cn/problems/check-if-strings-can-be-made-equal-with-operations-ii/description/

在这里插入图片描述

提示:
n == s1.length == s2.length
1 <= n <= 10^5
s1 和 s2 只包含小写英文字母。

只要两个字符串的奇偶位的各个字符的数量相等,就一定可以通过交换位置换成相同的字符串。

class Solution {
    public boolean checkStrings(String s1, String s2) {
        // 分别记录两个字符串的奇偶位字符数量
        int[] cnt1 = new int[26], cnt2 = new int[26], cnt3 = new int[26], cnt4 = new int[26];
        int n = s1.length();
        for (int i = 0; i < n; ++i) {
            if (i % 2 == 0) {
                cnt1[s1.charAt(i) - 'a']++;
                cnt3[s2.charAt(i) - 'a']++;
            } else {
                cnt2[s1.charAt(i) - 'a']++;
                cnt4[s2.charAt(i) - 'a']++;
            }
        }
        // 比较是否相等
        return Arrays.equals(cnt1, cnt3) && Arrays.equals(cnt2, cnt4);
    }
}

Q3:2841. 几乎唯一子数组的最大和

https://leetcode.cn/problems/maximum-sum-of-almost-unique-subarray/

在这里插入图片描述
提示:
1 <= nums.length <= 2 * 10^4
1 <= m <= k <= nums.length
1 <= nums[i] <= 10^9

竞赛时代码——滑动窗口

滑动窗口,维护窗口内的总和以及独特元素数量。

class Solution {
    public long maxSum(List<Integer> nums, int m, int k) {
        Map<Integer, Integer> cnt = new HashMap<>();
        int n = nums.size();
        long sum = 0, ans = 0;
        // 双指针+滑动窗口
        for (int i = 0, j = 0; i < n; ++i) {
            // 加入元素
            cnt.merge(nums.get(i), 1, Integer::sum);
            sum += nums.get(i);
            // 移除元素
            if (i - j >= k) {       
                cnt.merge(nums.get(j), -1, Integer::sum);
                sum -= nums.get(j);
                if (cnt.get(nums.get(j)) == 0) cnt.remove(nums.get(j));
                j++;
            }
            // 如果是几乎唯一子数组,就尝试更新答案
            if (i - j + 1 == k && cnt.size() >= m) ans = Math.max(ans, sum);
        }
        return ans;
    }
}

Q4:8050. 统计一个字符串的 k 子序列美丽值最大的数目(贪心+计数+组合数学)

https://leetcode.cn/problems/count-k-subsequences-of-a-string-with-maximum-beauty/
在这里插入图片描述

提示:
1 <= s.length <= 2 * 10^5
1 <= k <= s.length
s 只包含小写英文字母。

竞赛时代码(赛时通过,结束之后被rejudge了)🤯

贪心地选取出现次数最多的字符。

如果若干个字符的出现次数一样,那么就把答案乘上对应的组合数。

比如从 0,1,1,1,2,3 中取 k 为 4,那么 3 和 2 是一定要被选择的,然后在 3 个 1 中任意选择 2 个 1 即可。答案为 3 * 2 * 1 * C(3,2) = 18。

class Solution {
    public int countKSubsequencesWithMaxBeauty(String s, int k) {
        if (k > 26) return 0;

        long[] cnt = new long[26];          // 记录各个字符的数量
        for (char ch: s.toCharArray()) {
            cnt[ch - 'a']++;
        }
        Arrays.sort(cnt);                   // 按字符数量排序
        long ans = 1, MOD = (long)1e9 + 7;
        long mn = cnt[26 - k];              // 可选择的出现次数最少的字符出现次数
        ans = mn;
        // 求组合数是几选几 
        int end = -1;
        for (int i = 26 - k + 1; i < 26; ++i) {
            if (cnt[i] != mn) {
                if (end == -1) end = i;
            }
            ans = (ans * cnt[i]) % MOD;
        }
        if (end == -1) end = 26;
        for (int i = 0; i < end; ++i) {
            if (cnt[i] == mn) {
                int x = end - i;
                long y = op(x, k - 26 + end);   // 求组合数
                ans = (ans * y) % MOD;
                break;
            }
        }
        return (int)ans;
    }
    
    // 求组合数C(x, y)
    public long op(int x, int y) {
        long ans = 1, m = 1;
        while (y != 0) {
            ans *= x;
            m *= y;
            x--;
            y--;
        }
        return ans / m;
    }
}

错误的样例是

"xzkilqoqfdnjextrgqpywahbmsu"
23

预期结果是 132,但是我的代码返回了 0。

原因可能是因为没有考虑到 mn = 0,那后续怎么乘都还是 0。

正确解法⭐

在这里插入图片描述
这种做法的好处是枚举更清晰,代码逻辑也不会混乱。

Q:为什么 k 太大就不存在合法子序列?
A:因为 k 太大时,独特的字母的数量会不够用。

class Solution {
    final long MOD = (long)1e9 + 7;

    public int countKSubsequencesWithMaxBeauty(String s, int k) {
        // 统计每种字符出现的次数
        int[] cnt = new int[26];
        for (char ch: s.toCharArray()) {
            cnt[ch - 'a']++;
        }
        // 统计每种出现次数,以及多少种字符是这种次数
        TreeMap<Integer, Integer> cc = new TreeMap<>();
        for (int c: cnt) {
            if (c > 0) cc.merge(c, 1, Integer::sum);
        }

        long ans = 1;
        // .descendingMap()返回按键降序排序
        for (Map.Entry<Integer, Integer> e: cc.descendingMap().entrySet()) {    
            int c = e.getKey(), num = e.getValue();
            if (num >= k) {
                // 乘上 c^k 和 C(num, k)
                return (int) (ans * pow(c, k) % MOD * comb(num, k) % MOD);
            }
            ans = ans * pow(c, num) % MOD;
            k -= num;
        }
        return 0;       // k太大,没有那么多种字符
    }

    // 计算 x^n 快速幂
    long pow(long x, int n) {
        long res = 1;
        // 把 n 看成二进制数字,哪些位置是 1,就把它乘起来就好了
        for (; n > 0; n /= 2) {
            if (n % 2 == 1) res = res * x % MOD;
            x = x * x % MOD;
        }
        return res;
    }

    // 计算 C(n,k)
    long comb(long n, long k) {
        long res = n;
        for (int i = 2; i <= k; ++i) {
            res = res * --n / i;
        }
        return res % MOD;
    }
}

关于快速幂可见:【算法基础:数学知识】4.4 快速幂

成绩记录

在这里插入图片描述

就,还好。
WA 次数有点多。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
判断一个链表是否是另一个链表的子序列是一道常见的问题。对于这个问题,我们可以使用双指针的方法来解决。双指针一个指向主链表,一个指向子序列链表。我们同时遍历两个链表,比较指针指向的节点是否相同。如果相同,我们就同时向后移动两个指针;如果不相同,我们只移动主链表的指针。当子序列链表遍历完毕时,说明所有的节点都匹配成功,那么它是主链表的子序列;如果主链表遍历完毕,而子序列链表还没有遍历完,说明子序列链表中的节点没有完全匹配,那么它不是主链表的子序列。这种方法的时间复杂度是O(n + m),其中n是主链表的长度,m是子序列链表的长度。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [力扣之判断一个链表是否是回文链表](https://blog.csdn.net/chenbaifan/article/details/121450273)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [[力扣] 203.移除链表元素](https://download.csdn.net/download/weixin_38667920/13759251)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Wei *

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

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

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

打赏作者

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

抵扣说明:

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

余额充值