leetcode第186场周赛

目录

 

第一题、分割字符串的最大得分

第二题、可获得的最大点数

第三题、对角线遍历

第四题、带限制的子序列和


leetcode第186场周赛,时间2020/04/26 10:30-12:00

第一题、分割字符串的最大得分

难度:easy,链接:https://leetcode-cn.com/problems/maximum-score-after-splitting-a-string/

给你一个由若干 0 和 1 组成的字符串 s ,请你计算并返回将该字符串分割成两个 非空 子字符串(即 左 子字符串和 右 子字符串)所能获得的最大得分。

「分割字符串的得分」为 左 子字符串中 0 的数量加上 右 子字符串中 1 的数量。

给你一个由若干 0 和 1 组成的字符串 s ,请你计算并返回将该字符串分割成两个 非空 子字符串(即 左 子字符串和 右 子字符串)所能获得的最大得分。

「分割字符串的得分」为 左 子字符串中 0 的数量加上 右 子字符串中 1 的数量。

思路:暴力即可,用一个变量存储左边0的数量,用一个变量存储右边1的数量,然后遍历。

class Solution {
    public int maxScore(String s) {
        int num1 = 0;//右边1的数量,最开始左边为空,右边是整个字符串,所以计算出整个字符串1的数量
        for (int i = 0; i < s.length(); i++){
            if (s.charAt(i) == '1') num1++;
        }
        int num0 = 0;//左边0的数量
        int res = 0;//结果
        for (int i = 0; i < s.length() - 1; i++){
            if (s.charAt(i) == '0'){
                num0++;
            }
            else {
                num1--;
            }
            res = Math.max(res,num0 + num1);
        }
        return res;
    }
}

 

第二题、可获得的最大点数

难度:medium,链接:https://leetcode-cn.com/problems/maximum-points-you-can-obtain-from-cards/

几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。

每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。

你的点数就是你拿到手中的所有卡牌的点数之和。

给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数。

输入:cardPoints = [1,2,3,4,5,6,1], k = 3
输出:12
解释:第一次行动,不管拿哪张牌,你的点数总是 1 。但是,先拿最右边的卡牌将会最大化你的可获得点数。最优策略是拿右边的三张牌,最终点数为 1 + 6 + 5 = 12 。

思路:题目即等价为从数组[cardPoints[len - k], cardPoints[len - k],cardPoints[len - k],...cardPoints[len - 1],cardPoints[0],cardPoints[1],...cardPoints[k - 1]]中找出大小为k的子数组中和最大的,用滑动窗口滑动一下即可。代码如下:

class Solution {
    public int maxScore(int[] cardPoints, int k) {
        int len = cardPoints.length;
        int res = 0;
        int sum = 0;
        int start = len - k;
        int end = len - 1;
        for (int i = start; i <= end; i++){
            sum += cardPoints[i];
        }
        res = sum;
        for (int i = 1; i <= k; i++){
            end = (end + 1) % len;
            sum += cardPoints[end] - cardPoints[start];
            res = Math.max(res,sum);
            start = (start + 1) % len;
        }
        return res;
    }
}

 

第三题、对角线遍历

难度:medium,链接:https://leetcode-cn.com/problems/diagonal-traverse-ii/

给你一个列表 nums ,里面每一个元素都是一个整数列表。请你依照下面各图的规则,按顺序返回 nums 中对角线上的整数。

输入:nums = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,4,2,7,5,3,8,6,9]

思路:题目让输出对角线上的整数,而同一对角线的不同数的共同点是row+col相同,因为又要排序,所以结果数组输出应该为(row+col等于0的数,row+col等于1的数,row+col等于2的数。。。)。所以直接遍历矩阵,根据矩阵的行列放入一个二维数组中即可,然后最后将二维数组取出来。然后再看按这种思路会得到什么结果

[[1],[2,4],[3,5,7],[6,8],[9]],每个行加列相同的集合与结果输出的顺序相反,这是因为遍历的时候因为先遍历到行小的元素,所以行小的元素放在前面,但是结果却是行小的元素放在最后,所以只要将每个行加列相等的集合逆序一下即可。

代码如下:

class Solution {
    public int[] findDiagonalOrder(List<List<Integer>> nums) {
        int count = 0;//计算有多少数,以便最后res数组申请空间
        ArrayList<ArrayList<Integer>> arr = new ArrayList();
        for (int i = 0; i < nums.size(); i++)
        {
            for (int j = 0 ; j < nums.get(i).size(); j++)
            {
                if ((i + j + 1) > arr.size()){
                    arr.add(new ArrayList());
                    
                } 
                arr.get(i + j).add(nums.get(i).get(j));
            }
            count += nums.get(i).size();
        }
        int[] res = new int[count];
        int index = 0;
        for (int i = 0; i < arr.size(); i++)
        {
            for (int j = arr.get(i).size() - 1 ; j >= 0; j--)
            {
                res[index++] = arr.get(i).get(j);
            }
            
        }
        return res;
    }
}

 

第四题、带限制的子序列和

难度:hard,链接:https://leetcode-cn.com/problems/constrained-subset-sum/

给你一个整数数组 nums 和一个整数 k ,请你返回 非空 子序列元素和的最大值,子序列需要满足:子序列中每两个 相邻 的整数 nums[i] 和 nums[j] ,它们在原数组中的下标 i 和 j 满足 i < j 且 j - i <= k 。

数组的子序列定义为:将数组中的若干个数字删除(可以删除 0 个数字),剩下的数字按照原本的顺序排布。

给你一个整数数组 nums 和一个整数 k ,请你返回 非空 子序列元素和的最大值,子序列需要满足:子序列中每两个 相邻 的整数 nums[i] 和 nums[j] ,它们在原数组中的下标 i 和 j 满足 i < j 且 j - i <= k 。

数组的子序列定义为:将数组中的若干个数字删除(可以删除 0 个数字),剩下的数字按照原本的顺序排布。

 

我的思路:看到最大子序列和就很容易想到用动态规划来解决。动态规划递归式也很容易想出来。

dp[i] 为以nums[i]为结尾的子序列的最大元素和,又因为子序列中的上一个元素必然在{nums[i - 1],nums[i - 2],...,nums[i - k]}中,所以

dp[i] = max(dp[i - 1] + nums[i],dp[i - 2] + nums[i],dp[i - 3] + nums[i],...,dp[i - k] + nums[i])

所以可以写出对应代码:

class Solution {

    public int constrainedSubsetSum(int[] nums, int k) {

        int[] dp = new int[nums.length];

        dp[0] = nums[0];

        int res = dp[0];

        for (int i = 1; i < nums.length; i++)

        {

            dp[i] = nums[i];

            for (int j = Math.max(0,i - k); j < i; j++){

                dp[i] = Math.max(dp[i],dp[j] + nums[i]);

            }

            res = Math.max(res,dp[i]);

        }

        return res;

    }

}

分析时间复杂度,因为k <= 数组大小,所以最坏情况下k可能会等于数组大小,假设数组大小为N,最坏情况下时间复杂度会达到O(n^2),run了之后果然超时了,所以需要优化。

回到原来的动态规划递归式:

dp[i] = max{dp[i - 1] + nums[i],dp[i - 2] + nums[i],dp[i - 3] + nums[i],...,dp[i - k] + nums[i]}

 

即dp[i] = max{dp[i-1] , dp[i - 2] , dp[i - 3] , ... , dp[i - k]} + nums[i] 

 

尝试化简,写出dp[i - 1]的递推式,看看两者是否有关系,可否从dp[i - 1]推到dp[i]

 

dp[ i - 1] = max{dp[i - 2] , dp[i - 3] , ... , dp[i - k] ,dp[i - k - 1]} + nums[i - 1]

 

尝试相减,不能直接相减,找两者的共同点,发现两者都有一段max {dp[i - 2],dp[i - 3] , dp[ i - k]},再将两个dp递推式转化一下:

 

dp[i] - nums[i] = max{dp[i-1] ,** max {dp[i - 2] , dp[i - 3] , ... , dp[i - k]}**} 

 

dp[i - 1] - nums[i - 1] = max{**max{dp[i - 2] , dp[i - 3] , ... , dp[i - k]}** ,dp[i - k - 1]} 

 

找到了共同点之后发现不能直接从dp[i - 1]推到dp[i],但观察一下,dp[i - 1]就是找dp[i- k - 1]到dp[ i - 2]的最大值,dp[i] 就是找dp[i - k]到dp[i - 1]的最大值,那这不就是大小为k的滑动窗口吗,用单调队列即可以O(n)的时间复杂度求出所有窗口的最大值。

lc里也有这道题:https://leetcode-cn.com/problems/sliding-window-maximum/

 

代码如下:

```

class Solution {

    public int constrainedSubsetSum(int[] nums, int k) {

        int[] dp = new int[nums.length];

        dp[0] = nums[0];

        int res = dp[0];

        Deque<Integer> deque = new ArrayDeque();//单调递减队列,队首是窗口的最大值,

        deque.addFirst(dp[0]);

        for (int i = 1; i < k; i++) //当窗口大小没有达到k的时候

        {

            dp[i] = Math.max(deque.getFirst() + nums[i] , nums[i]);

            res = Math.max(res,dp[i]);

            while (!deque.isEmpty() && deque.getLast() < dp[i])

            {

                deque.removeLast();

            }

            deque.addLast(dp[i]);

        }

        for (int i = k; i < nums.length; i++)//当窗口大小达到k后

        {

            dp[i] = Math.max(deque.getFirst() + nums[i] , nums[i]);

            res = Math.max(res,dp[i]);

            while (!deque.isEmpty() && deque.getLast() < dp[i])

            {

                deque.removeLast();

            }

            deque.addLast(dp[i]);

            if (dp[i - k] == deque.getFirst()) deque.removeFirst();

        }

        return res;

    }

}

```

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章