LeetCode第31场双周赛题解

5456.在区间范围内统计奇数数目

题干: 给你两个非负整数 low 和 high 。请你返回 low 和 high 之间(包括二者)奇数的数目。
LeetCode第31场双周赛题解_第1张图片
思路: 该题如果low和high都为偶数,返回(high-low)/2。若low和high有一个不为偶数,那么便+1;

//Java代码
class Solution {
    public int countOdds(int low, int high) {
        if(low%2==0&&high%2==0)
            return (high-low)/2;
        else
            return (high-low)/2+1;
    }
}

总结: 简单签到水题。

1524. 和为奇数的子数组数目

题干: 给你一个整数数组 arr 。请你返回和为 奇数 的子数组数目。
由于答案可能会很大,请你将结果对 10^9 + 7 取余后返回。
LeetCode第31场双周赛题解_第2张图片
LeetCode第31场双周赛题解_第3张图片
思路: 比较明显的前缀和问题。
比赛时候我本来想用O(n2)复杂度的两次遍历方法(如下)

//O(n^2)复杂度算法
class Solution {
    public int numOfSubarrays(int[] arr) {
        int len=arr.length;
        long[] sum=new long[len+1];
        int mod=(int)Math.pow(10,9)+7;
        for(int i=1;i<=len;i++){
            sum[i]+=arr[i-1]+sum[i-1];
        }
        long ans=0;
        for(int i=0;i<=len;i++){
            for(int j=i+1;j<=len;j++){
                if((sum[j]-sum[i])%2==1)
                {
                    ans=(ans+1)%mod;
                }
            }
        }
        return (int)ans%mod;
    }
}

毫无意外的T了,然后我就思考如何优化。
然后我们可以很容易想到,还是前缀和,若 0~i 这个子数组的前缀和值为奇数,那么其他前缀和值为偶数的子数组都可以与之构成符合条件的子数组,反之亦然。
如下

//Java代码
class Solution {
    public int numOfSubarrays(int[] arr) {
        int len=arr.length;
        long[] sum=new long[len+1];
        int mod=(int)Math.pow(10,9)+7;
        long l=1;
        long r=0;
        for(int i=1;i<=len;i++){
            sum[i]+=arr[i-1]+sum[i-1];
            if(sum[i]%2==0)
                l++;
            else
                r++;
        }
        long ans=(l*r)%mod;
        return (int)ans;
    }
}

总结: 有点绕,测试的时候想的时间太长了。

1525. 字符串的好分割数目

题干: 给你一个字符串 s ,一个分割被称为 「好分割」 当它满足:将 s 分割成 2 个字符串 p 和 q ,它们连接起来等于 s 且 p 和 q 中不同字符的数目相同。

请你返回 s 中好分割的数目。
LeetCode第31场双周赛题解_第4张图片
LeetCode第31场双周赛题解_第5张图片
思路: 这个题个人认为比第二题简单。我用了比较蠢笨的方法直接写出来了。因为题目要求是两个子字符串不同的字符数目相等。那么我们直接循环求出每个位置的不同字符数目即可。如下:

//Java代码
class Solution {
    public int numSplits(String s) {
        int len=s.length();
        int[] sum1=new int[len+1];
        int[] sum2=new int[len+1];
        boolean[] vis1=new boolean[26];
        boolean[] vis2=new boolean[26]; 
        for(int i=0;i<len;i++){
            int ch=s.charAt(i)-'a';
            sum1[i+1]=sum1[i];
            if(!vis1[ch])
            {
                vis1[ch]=true;
                sum1[i+1]++;
            }
        }
        for(int i=len-1;i>=0;i--){
            int ch=s.charAt(i)-'a';
            sum2[i]=sum2[i+1];
            if(!vis2[ch]){
                vis2[ch]=true;
                sum2[i]++;
            }
        }
        int ans=0;
        for(int i=1;i<=len;i++){
            if(sum1[i]==sum2[i])
                ans++;
        }
        return ans;
    }
}

总结: 感觉可以优化。

1526. 形成目标数组的子数组最少增加次数

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

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

在 initial 中选择 任意 子数组,并将子数组中每个元素增加 1 。
答案保证在 32 位有符号整数以内。

LeetCode第31场双周赛题解_第6张图片
LeetCode第31场双周赛题解_第7张图片
此题有多种解法。这里介绍单调栈,此外还有种差分的解法,还可以直接线段树写出。但是差分和线段树我太久没用,忘记了,所以如果感兴趣,请自行百度。
思路: 因为处于第0位的 target[0] 肯定需要 target[0] 次操作,那么 ans 初始值设置为 target[0] 。然后后序的便是单调栈的思想,ans加上0与后一位与前一位的差值中的max值,最后ans即为答案。

//java代码
class Solution {
    public int minNumberOperations(int[] target) {
        int ans=target[0],len=target.length;
        for(int i=1;i<len;i++){
            ans+=Math.max(target[i]-target[i-1],0);
        }
        return ans;
    }
}

总结: 需要巩固单调栈、线段树、差分等算法。

赛后总结

此处比赛比较偏向于算法的应用,如前缀和、单调栈、线段树、差分等,需要加强这些算法的理解与熟练。
并且该次比赛,我提交次数过多导致排名偏低。在这里插入图片描述
所以以后写题不要抖机灵,想啥写啥。

你可能感兴趣的:(LeetCode)