leetcode双周赛第31场,如何把做过的题做多

leetcode双周赛第31场,如何把做过的题做多

      • 题目一:在区间内统计奇数数目(题号:5456)
        • python代码
        • 使用range函数的代码
      • 题目二:和为奇数的子数组数目(题号:5457)
        • python代码
        • python代码如下
      • 后两题链接

  以前没玩过leetcode的比赛,第一次玩,感觉几个题当中还是有值得整理的地方。类似的题目别人也会写,但是大家都是描述题目,然后写代码,希望我的文章能够完整展示思考的过程,希望你能够有助于帮助大家建立良好的思维习惯,提高思维能力。如此,不甚喜悦。
  水平有限,思考不值得借鉴的地方也希望大家自行甄别,如果能给出建议,不胜感激。
  有的人做完题目,这道题也就到此结束了,但是我们需要具备把题做多的能力,做完一个题,可以稍微的想想类似的题目该如何做,如果我是出题人,这道题可以怎么变。

题目一:在区间内统计奇数数目(题号:5456)

给你两个非负整数 low 和 high 。请你返回 low  high 之间(包括二者)奇数的数目。

 

示例 1:

输入:low = 3, high = 7 输出:3
解释:3 到 7 之间奇数数字为 [3,5,7] 。

示例 2:

输入:low = 8, high = 10 输出:1
解释:8 到 10 之间奇数数字为 [9] 。

 

提示:

  • 0 <= low <= high <= 10^9

  这个题目很简单,确实是easy的难度,做法相当多,基本大同小异。这里就简单分析下。奇数的个数显然是每两个数出现一次,肯定要计算high和low之间有多少个数,然后除2,这部分是核心部分,但是包括两个端点就增加了一些困难。
  因为两个数的奇偶性会对结果产生巨大影响,所以干脆想个办法将情况统一一下。事实上再细致分析,只需要对low考虑奇偶性即可。因为可以使用除法的取整,将hing的奇偶性给忽略掉。

python代码

class Solution:
    def countOdds(self, low: int, high: int) -> int:
        if low%2==0:
        # 这里的判断,之后为了将两种情况统一起来
            low +=1
        return (high-low)//2 + 1

  为什么说判断是为了将情况统一起来,显然low是偶数的时候,low是不算的,从low+1开始算起,这样将low转换为奇数后,返回答案即可。
  这里还可以利用上python3语言的特性,python3的range函数因为自身是惰性计算,且底层实现比自己实现的要快,所以使用range函数提交,速度更快。代码如下。

使用range函数的代码

class Solution:
    def countOdds(self, low: int, high: int) -> int:
        if not low&1:
            low +=1
        return len(range(low,high+1,2))

题目二:和为奇数的子数组数目(题号:5457)

给你一个整数数组 arr 。请你返回和为 奇数 的子数组数目。

由于答案可能会很大,请你将结果对 10^9 + 7 取余后返回。

 

示例 1:

输入:arr = [1,3,5] 输出:4
解释:所有的子数组为 [[1],[1,3],[1,3,5],[3],[3,5],[5]] 。
所有子数组的和为 [1,4,9,3,8,5]. 奇数和包括 [1,9,3,5] ,所以答案为 4 。 

示例 2 :

输入:arr = [2,4,6] 输出:0
解释:所有子数组为 [[2],[2,4],[2,4,6],[4],[4,6],[6]] 。 所有子数组和为
[2,6,12,4,10,6] 。 所有子数组和都是偶数,所以答案为 0 。 

示例 3:

输入:arr = [1,2,3,4,5,6,7] 输出:16

示例 4:

输入:arr = [100,100,99,99] 输出:4

示例 5:

输入:arr = [7] 输出:1 

 

提示:

  • 1 <= arr.length <= 10^5
  • 1 <= arr[i] <= 100

  这个题目稍稍有点意思,做的时候没看清楚也没想到子数组需要是连续的一段,所以做错了,不过这就给这个题目有了扩展的余地。先来分析子数组的情况。
  为了节省篇幅,直接来讲解动态规划如何进行。显然子数组的情况必然要到一个数截止,而这个数的奇偶性会对结果产生影响。
  我们可以计算到第i个数为止,和为奇数的子数组数目。这样我们就可以使用第i-1的结果计算了,这就是定义动态规划的思路。
  那么分两种情况, d p i dp_i dpi就和第i个数的奇偶性有关。如果arr[i]是奇数,那么显然和到第i-1个数为止,构成的偶数和子数组有关。如果是偶数,那么和到第i-1个数为止,构成的奇数和子数组有关。所以我们不仅需要计算奇数和子数组,还要计算偶数和子数组。
  我们同时定义 d p o i dpo_i dpoi为到第i个数为止,和为偶数的子数组数目,则 d p o i dpo_i dpoi的计算和 d p i dp_i dpi是类似的,无非是奇偶性不同。
  所以问题变成了判断第i个数的奇偶性,如果为偶数,则到此为止的奇数和子数组不变,偶数和子数组加1。如果第i个数为奇数,则到此为止的偶数和子数组等于到第i-1个数的奇数和子数组。而到此为止的奇数和子数组等于到第i-1个数的偶数和子数组再加1。两个加1都是因为可以把第i个数字,单独拿出来作为一个子数组。

python代码

class Solution:
    def numOfSubarrays(self, arr: List[int]) -> int:
        res, even, odd = 0, 0, 0
        mod = int(1E9 + 7)
        for i in arr:
            even = (even + 1) % mod
            if i & 1:
                even, odd = odd, even
            res = (res + odd) % mod
        return res

  因为动态规划只和前一个状态有关,所以空间复杂度自然的优化到 O ( 1 ) O(1) O(1),而时间复杂度为 O ( n ) O(n) O(n)
  接下来就是开头埋得坑了,如果这个题目把子数组的限制改成了子集。也就是说不要求选出的子集连续了,且集合没有相同的数。这个时候问题又该如何处理的。
  显然,如果不要求连续,则分析构成奇数和的子集,奇数的个数一定是奇数,而偶数的个数可以是任意的。
  所以只需要统计数组中奇数的个数和偶数的个数分别是多少。接下来就是一个排列组合问题了。偶数的个数,任意,那么有n个偶数,每个数都可以取或者不取,则偶数的取法有 2 n 2^n 2n种。假设奇数有m个,则奇数的取法可以取任意小于m的奇数。即
∑ k C m k k < = m 且 k 为 奇 数 \sum_{k}{C_m^k}\quad k<=m且k为奇数 kCmkk<=mk
  得到了偶数的取法,得到了奇数的取法,最后只需要对两者进行相乘即可。

python代码如下

class Solution:
    def numOfSubarrays(self, arr: List[int]) -> int:
        even, odd = 0, 0
        mod = int(1E9 + 7)
        for i in arr:
            if i & 1:
                odd += 1
            else:
                even += 1
        oddComb = 0
        evenComb = 1 << odd
        for i in range(1, odd + 1, 2):
            oddComb = int(oddComb + comb(odd, i)) % mod

        return oddComb * evenComb % mod

  多想想如果题目变了可以怎么做,这样不仅能拓宽思路,还能加深理解。
  鉴于后面的篇幅还是比较长,先写两道题,后面两道题见链接。

后两题链接

你可能感兴趣的:(算法,算法,数据结构,动态规划,python,leetcode)