20200727:力扣第31周双周赛题解

力扣第31周双周赛题解

题目一:在区间范围内统计奇数数目

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

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

示例

输入:low = 3,high = 7

输出: 3

解释:3到7之间的奇数有3,5,7共3个。

解题思路

思路无非两种,一,直接模拟,二,寻找规律。

不假思索的模拟方法为:分出四类,low high组合,奇偶,奇奇,偶偶,偶奇,即可完成代码。在不需要花太多时间的第一题如果你没思路,那么完全可以这样做,代码量也不大。

第二种方法就是在上一种思路上再推一步,我们直接固定下区间,分别获取到从0开始分别到low和high的范围内的所有奇数个数,再两者相减,即可得到我们的目标值。

以上述示例说明:0-3的奇数个数为3 / 2 + 1= 2 ,0-7的奇数个数为7/2 + 1 = 4,加不加1取决于高位这个数是否为奇数,若为奇数则加1,不是则不加。注意包含high和low本身的数,因此实际上我们相当于漏掉了low这个值的奇偶性,因此再加上一个low % 2即可。写入代码你会发现实际上会将low%2约掉,也就是说low和high之间的奇数总数跟low的奇偶没有关系。因此最终优化代码如下。

代码实现

class Solution {
	public int countOdds(int low,int high) {
		int Hcount = high / 2 + high % 2;
		// int Lcount = low / 2 + low % 2;
		int Lcount = low / 2 + low % 2;
		// return Hcount - Lcount;
		return Hcount - Lcount;
	}
}

题目二:和为奇数的子数组数目

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

答案比较大,因此请你返回将结果对10^9+7取余的结果。

1 <= arr.length <= 10^5

1 <= arr[i] <= 100

示例

输入: arr = [1,3,5]

输出: 4

解释:子数组为[[1],[3],[5],[1,3],[1,5],[3,5],[1,3,5]]共7个,

其和为1,3,5,4,6,8,9,

奇数包括1,3,5,9

共4个因此返回4.

解题思路

  1. 首先根据arr的长度为一万以内,因此可以排除暴力的方法。必定TLE。再仔细一看本题可以使用动态规划的解答。
  2. 具体如下:和为奇数的子数组的数目。那么对应一个和为偶数的子数组的数目,二者互斥。因此定义dp [i] [0]和dp [i]
    [1]分别为以arr[i]结尾的子数组和为奇数和偶数的数目。
  3. 考虑我们从前面往后面推,先给出base case为dp[0] [0] = (arr[i] % 2 == 1)
  4. 考虑状态转移:如果arr[i]为偶数,那么dp [i] [0] = dp [i - 1] [0],dp[i] [1] = dp [i -1] [1] + 1
    如果arr[i]为奇数,那么dp [i] [0] = dp [i - 1] [1] + 1,dp [i] [1] = dp [i - 1] [0] 。
  5. 最后统计dp[0] [0] + … + dp [arr.length - 1] [0]即为结果。

代码实现

class Solution {
    public int numOfSubarrays(int[] arr) {
        final int VAL = 1000000007;
        int res = 0;
        int[][] dp = new int[arr.length][2];
        // base case
        if (arr[0] % 2 == 1) {
            dp[0][0] = 1;
        } else {
            dp[0][1] = 1;
        }
        // 状态转移
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] % 2 == 0) {
                dp[i][0] = dp[i - 1][0];
                dp[i][1] = dp[i - 1][1] + 1;
            } else {
                dp[i][0] = dp[i - 1][1] + 1;
                dp[i][1] = dp[i - 1][0];
            }
        }
        // 结果
        for (int j = 0; j < arr.length; j++) {
            res += dp[j][0];
            res %= VAL;
        }
        return res;
    }
}

题目三:字符串的好分割数目

给你一个字符串s,一个分割被称为好分割的定义是:当它满足将s分为两个字符串p和q,连起来等于s,且p和q中不同字符的数目相同。

请你返回s中好分割的数目。

示例

输入:s = “aacaba”

输出:2

解释:aac aba和aaca,ba两种分法满足好分割的要求。返回2

解题思路

将字符串分为两部分,分别统计两部分的不同字母的数量,然后分割线从左边开始,找到好分割点则ans++即可。

代码实现

class Solution {
    public int numSplits(String s) {
        int res = 0;
        int lCount = 0,rCount = 0;
        // 初始化左右两边的数组,数组索引对应具体字母,数组值对应字母出现次数。
        int[] left = new int[26];
        int[] right = new int[26];
        
        // 分割线从左边开始,默认所有元素都在右边
        for (int i = 0; i < s.length(); i++) {
            int index = s.charAt(i) - 'a';
            if (right[index] == 0) {
                rCount++;
            }
            right[index]++;
        }
        
        // 开始移动分割线,每次移动一个元素到左边,先更新left数组,再更新lCount,再更新right数组,再更新rCount,最后再判断是否满足条件。
        for (int j = 0; j < s.length(); j++) {
            int index1 = s.charAt(j) - 'a';
            if (left[index1] == 0) {
                lCount++;
            }
            left[index1]++;
            right[index1]--;
            if (right[index1] == 0) {
                rCount--;
            }
            if (lCount == Rcount) {
                res++;
            }
        }
        return res;
    }
}

题目四:形成目标数组的子数组最少增加次数

给你一个整数数组target和一个数组initial,initial数组与target数组有同样的维度,且一开始全部为0。

请你返回从initial到target的最少操作次数,每次操作需遵循以下规则:

​ 在initial中选择任意子数组,并将子数组的每个元素+1。

答案保证在32位有符号整数以内。

示例

输入: target = [1,2,3,2,1]

输出: 3

解释:从[0,0,0,0,0] -> [1,1,1,1,1] ->[1,2,2,2,1] ->[1,2,3,2,1] 共3次。

解题思路

本题可以是一个波峰问题的原型题目,比如示例,给的就是一个山峰,从1爬山到3再下山到1,因为你需要从0开始加,且每次只能加1,那么思路就显而易见了,计算那个爬山或者下山的过程即可。我们以爬山为例进行代码实现即可。

代码实现

class Solution {
	public int minNumOperations(int[] target) {
        // 题目未限定target的长度,因此需要特数情况处理,注意细节
        if (target.length == 0 || target == null) {
            return 0;
        }
        
        int res = target[0];
        for (int i = 1; i < target.length; i++) {
            res += Math.max(target[i] - target[i - 1], 0);
        }  
        return res;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

IMMUNIZE

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

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

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

打赏作者

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

抵扣说明:

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

余额充值