首页 > 其他分享 >LeetCode 周赛上分之旅 #35 两题坐牢,菜鸡现出原形

LeetCode 周赛上分之旅 #35 两题坐牢,菜鸡现出原形

时间:2023-07-23 23:24:01浏览次数:38  
标签:周赛 现出原形 nums 复杂度 路径 35 ret path LeetCode

⭐️ 本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 和 [BaguTree Pro] 知识星球提问。

学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越抽象,它能覆盖的问题域就越广,理解难度也更复杂。在这个专栏里,小彭与你分享每场 LeetCode 周赛的解题报告,一起体会上分之旅。

本文是 LeetCode 上分之旅系列的第 35 篇文章,往期回顾请移步到文章末尾~

T1. 按分隔符拆分字符串(Easy)

  • 标签:模拟

T2. 合并后数组中的最大元素(Medium)

  • 标签:贪心

T3. 长度递增组的最大数目(Hard)

  • 标签:排序、贪心

T4. 树中可以形成回文的路径数(Hard)

  • 标签:状态压缩、前缀和、散列表


T1. 按分隔符拆分字符串(Easy)

https://leetcode.cn/problems/split-strings-by-separator/

题解(模拟)

简单模拟题。

class Solution:
    def splitWordsBySeparator(self, words: List[str], separator: str) -> List[str]:
        ans = []
        for x in words:
            for y in x.split(separator):
                if y != "":
                    ans.append(y)
        return ans

复杂度分析:

  • 时间复杂度:$O(L)$ 其中 $L$ 为字符总数;
  • 空间复杂度:$O(1)$ 不考虑结果数组,仅占用常量级别空间。

T2. 合并后数组中的最大元素(Medium)

https://leetcode.cn/problems/largest-element-in-an-array-after-merge-operations/

题解(贪心)

由于题目操作的前提是 nums[i] ≤ nums[i + 1],因此我们可以优先合并靠后的相邻序列,这样可以保证靠前的更多数能够被合并。如果中间出现 nums[i] 不小于 nums[i + 1] 的情况,说明遇到一个较大的数,它的权重大于后续数组的合并,我们则直接使用这个较大的数。

class Solution:
    def maxArrayValue(self, nums: List[int]) -> int:
        for i in range(len(nums) - 2, -1, -1):
            if (nums[i] <= nums[i + 1]): nums[i] += nums[i + 1]
        return nums[0]
class Solution {
    fun maxArrayValue(nums: IntArray): Long {
        val n = nums.size
        var ret = Long.MIN_VALUE
        for (i in n - 1 downTo 0) {
            if (ret >= nums[i]) {
                ret += nums[i]
            } else {
                ret = nums[i].toLong()
            }
        }
        return ret
    }
}

复杂度分析:

  • 时间复杂度:$O(n)$ 线性遍历;
  • 空间复杂度:$O(1)$ 仅使用常量级别空间。

T3. 长度递增组的最大数目(Hard)

https://leetcode.cn/problems/maximum-number-of-groups-with-increasing-length/

题解(排序)

输入数组相当于数字的出现频率,由于题目只关心构造组合的方案数而不关心数字的内容,数字本身是不重要的,因此我们可以先对频率数组排序,并从小到大枚举频率。

在构造的过程中,我们将当前遍历到的频率追加到已经拼接过的分组上(默认存在一个空分组),如果当前的频率不够或者超出,则将剩余元素放到候选容器中,严格证明见灵神的题解。

# 测试用例 [2, 2, 2]
0 => 0 1 => 0 1 2
       1      1 2
                0
# 测试用例 [1, 2, 5]
0 => 0 1 => 0 1 2
       1      1 2
                2
# 测试用例 [2, 1, 2] => 排序 [1, 2, 2]
0 => 0 1 => 0 1 2
       1      1 2
# 测试用例 [1,1]
0
# 测试用例 [2, 100, 2, 2, 2] => 排序 [2, 2, 2, 2, 100]
0 => 0 1 => 0 1 2 => 跳过 => 0 1 2 3 => 剩余的 4 可以拼接,但不会产生增加分组数量
       1      1 2             1 2 3
                0               0 4
                                  4
class Solution {
    fun maxIncreasingGroups(usageLimits: List<Int>): Int {
        Collections.sort(usageLimits)
        var ret = 0
        var left = 0L
        for (x in usageLimits) {
            left += x.toLong()
            // 可以构造新分组
            if (left >= ret + 1) {
                ret ++
                left -= ret
            }
        }
        return ret
    }
}

复杂度分析:

  • 时间复杂度:$O(nlgn)$ 瓶颈在排序;
  • 空间复杂度:$O(lgn)$ 瓶颈在排序。

T4. 树中可以形成回文的路径数(Hard)

https://leetcode.cn/problems/count-paths-that-can-form-a-palindrome-in-a-tree/

题解(状态压缩 + 前缀和 + 散列表)

1、回文判断: 首先,由于题目的回文串判断允许重排,因此回文串的 check 可以转换为字母的计数:

  • 出现次数为奇数的字母最多只能出现 1 个;
  • 出现次数为偶数的字母可以出现任意次。

2、奇偶性: 其次,由于题目的数组仅为小写字母,我们可以使用一个整型来压缩表示 26 个字母的出现次数状态,0 表示出现次数为偶数,1 表示出现次数为奇数。例如 0001 表示 ‘a’ 字母的出现次数为奇数,其他字母的出现次数为偶数(可能未出现)。

3、状态压缩: 基于以上 2 点,我们的目标是在树上找到两个点的路径 [u, v] 使得路径的状态 mask 满足以下其中 1 个条件:

  • mask == 0:说明所有字母都出现偶数次;
  • mask & (mask - 1) == 0:说明二进制位中 1 的出现次数为 1 次,即只有一个字母出现奇数次。

4、前缀和: 那么,如果如何求树上两点间的路径?这里有一个技巧,如果直接收集两个点之间的路径信息很难,我们可以先求从根节点到 u 的路径 root_u,以及从根节点到 v 的路径 root_v,再将两段路径异或就可以得到 u 到 v 的路径(如果两个节点的 LCA 不是根节点,那么重复的路径会被异或消除)。以题目示例 1 中节点 3 和节点 4 为例:path_3 = 0101,path_4 = 0110,而 path_3 xor path_4 = 0011,即 “ab”。

5、两数之和: 最后,我们的目标就变成:寻找从到根节点的路径异或值满足「分析 3」条件的路径,这可以用类似「两数之和」中的散列表的方法求解。

class Solution {
    fun countPalindromePaths(parent: List<Int>, s: String): Long {
        var ret = 0L
        val cnt = HashMap<Int, Int>()
        val memo = HashMap<Int, Int>()
        // 两数之和模板
        for (i in 0 until parent.size) {
            val path = getPath(parent, s, memo, i)
            // 1、两条路径异或值等于 0 的情况
            ret += cnt.getOrDefault(path, 0) 
            for (j in 0 until 26) {
                // 2、两条路径异或值中二进制位 1 只有 1 个的情况
                ret += cnt.getOrDefault(path xor 1.shl(j), 0)
            }
            cnt[path] = cnt.getOrDefault(path, 0) + 1
        }
        return ret
    }

    private fun getPath(parent: List<Int>, s: String, memo: MutableMap<Int, Int>, i: Int): Int {
        // 终止条件
        if (i == 0) return 0
        // 读备忘录
        if (memo.containsKey(i)) return memo[i]!!
        val path = getPath(parent, s, memo, parent[i]) xor (1.shl(s[i] - 'a')) // 增加一个计数等于奇偶性翻转
        // 存备忘录
        memo[i] = path
        return path
    }
}

复杂度分析:

  • 时间复杂度:$O(Cn)$ getPath() DFS 的时间复杂度为 $O(n)$,枚举方案的时间复杂度为 $O(Cn)$,其中 $C$ 为字符集大小;
  • 空间复杂度:$O(n)$ 记录每个节点到根节点的路径值空间。

推荐阅读

LeetCode 上分之旅系列往期回顾:

⭐️ 永远相信美好的事情即将发生,欢迎加入小彭的 Android 交流社群~

标签:周赛,现出原形,nums,复杂度,路径,35,ret,path,LeetCode
From: https://www.cnblogs.com/pengxurui/p/17576176.html

相关文章

  • 牛客周赛Round4(java)
     Java组代码importjava.util.Scanner;publicclassMain{publicstaticvoidmain(String[]args){Scannerscanner=newScanner(System.in);intn=scanner.nextInt();intm=scanner.nextInt();StringBuildersb=newStringB......
  • leetcode第 109 场双周赛
    6930.检查数组是否是好的-力扣(LeetCode)首先判断数组长度是不是最大值+1,然后排个序,判断0到n-2是不是都是1到最大值的一个排列,满足这些返回true就行了classSolution{public:boolisGood(vector<int>&num){intma=0;for(autoi:num){......
  • 35 pinctrl(一)简介.md
    1.简介pinctrl:即pincontroller引脚控制。对应设备的iomux和config模块2.pinctrol作用引脚的枚举和命名列出所以的设备的引脚,并对其进行命名引脚复用指定引脚复用情况。复用为GPIO还是IIC或者其他引脚配置配置引脚的基本属性。上拉,下拉,开漏等3.pinctrol学......
  • P3352 [ZJOI2016] 线段树 思考--zhengjun
    有一个显然的\(O(n^3q)\)的做法:设\(f_{i,l,r,x}\)表示\(i\)次操作过后,区间\([l,r]\)的数\(\lex\),\(a_{l-1},a_{r+1}>x\)的方案数。转移:$$f_{i,l,r,x}=f_{i-1,l,r,x}\timesg_{l,r}+\sum\limits_{j<l}f_{i-1,j,r,x}\times(j-1)+\sum\limits_{j>r}f_{i-1,l......
  • 【大联盟】20230706 graph(graph) QOJ4635 【Graph Operation】
    题解赛时得分:60/?写了个乱搞首先考虑无解的条件。注意到一次操作后,所有点的度数都没有改变,所以无解的充分条件就是存在一个点的度数在两张图中不相等。接下来尝试构造策略,使得度数相等的时候都能出解。我们可以将题意转化一下,变为对图\(G\)和图\(H\)都可以操作,使得最后产......
  • 剑指 Offer 35. 复杂链表的复制
    题目:/*//DefinitionforaNode.classNode{public:intval;Node*next;Node*random;Node(int_val){val=_val;next=NULL;random=NULL;}};*/classSolution{public:Node*copyRandomList(N......
  • P9352 题解
    problem&blog。HerryHuang的DP专题中最喜欢的一题,抢第一篇题解/fendou。关于题意:只有往猫那里扔路障,猫才会动,否则只会原地坐牢。猫如果要走动,是一下子走到最高点,而不是慢慢挪动。假设猫在\(u\)点。现在往\(u\)扔路障,猫会跑去最高点,然后他无法返回到\(u\)的其他子......
  • 咚咚咚————【封装驱动】Si5351A方波信号发生器发送任意(8K-160Mhz)频率程序
    咚咚咚————【封装驱动】Si5351A方波信号发生器发送任意[8K-160Mhz]频率程序(一)效果展示(二)源码分享(三)需要改进的地方及不足(使用阿波罗STM32F7开发板)(一)效果展示(二)源码分享芯片SI5351A源代码下载可以支持一下吗QAQSI5351A.c/*****************......
  • Si5351时钟芯片控制
    Si5351一、SI5351频率计算公式:f(out)=f(pl......
  • TQ3568 Buildroot文件系统终端上支持中文显示调试方法
    TQ3568Buildroot文件系统终端上支持中文显示调试方法修改busybox配置单如果是buildroot则makebusybox-menuconfigARCH=arm64diff--gita/rootfs/buildroot/package/busybox/busybox.configb/rootfs/buildroot/package/busybox/busybox.configindex02b1ee1..abc857e10064......