LeetCode 504. 七进制数 / 2055. 蜡烛之间的盘子 / 798. 得分最高的最小轮调(差分数组)

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 504. 七进制数 / 2055. 蜡烛之间的盘子 / 798. 得分最高的最小轮调(差分数组)相关的知识,希望对你有一定的参考价值。

504. 七进制数

2022.3.7 每日一题

题目描述

给定一个整数 num,将其转化为 7 进制,并以字符串形式输出。

示例 1:

输入: num = 100
输出: “202”

示例 2:

输入: num = -7
输出: “-10”

提示:

-10^7 <= num <= 10^7

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/base-7
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

class Solution 
    public String convertToBase7(int num) 
        if(num == 0)
            return "0";
        boolean flag = false;   //标记正负数
        if(num >= 0)
            flag = true;
        //转成正数
        if(!flag) num = -num;

        StringBuilder sb = new StringBuilder();

        while(num != 0)
            sb.append(String.valueOf(num % 7));
            num = num / 7;
        

        if(!flag) 
            sb.append("-");
        sb.reverse();

        return sb.toString();

    

2055. 蜡烛之间的盘子

2022.3.8 每日一题,祝所有女性朋友节日快乐,永远年轻快乐

题目描述

给你一个长桌子,桌子上盘子和蜡烛排成一列。给你一个下标从 0 开始的字符串 s ,它只包含字符 ‘’ 和 ‘|’ ,其中 '’ 表示一个 盘子 ,’|’ 表示一支 蜡烛 。

同时给你一个下标从 0 开始的二维整数数组 queries ,其中 queries[i] = [lefti, righti] 表示 子字符串 s[lefti…righti] (包含左右端点的字符)。对于每个查询,你需要找到 子字符串中 在 两支蜡烛之间 的盘子的 数目 。如果一个盘子在 子字符串中 左边和右边 都 至少有一支蜡烛,那么这个盘子满足在 两支蜡烛之间 。

比方说,s = “|||||" ,查询 [3, 8] ,表示的是子字符串 "||**|” 。子字符串中在两支蜡烛之间的盘子数目为 2 ,子字符串中右边两个盘子在它们左边和右边 都 至少有一支蜡烛。
请你返回一个整数数组 answer ,其中 answer[i] 是第 i 个查询的答案。

示例 1:


输入:s = “||***|”, queries = [[2,5],[5,9]]
输出:[2,3]
解释:
-queries[0] 有两个盘子在蜡烛之间。
-queries[1] 有三个盘子在蜡烛之间。

示例 2:


输入:s = “||||||*”, queries = [[1,17],[4,5],[14,17],[5,11],[15,16]]
输出:[9,0,0,0,0]
解释:
-queries[0] 有 9 个盘子在蜡烛之间。
-另一个查询没有盘子在蜡烛之间。

提示:

3 <= s.length <= 10^5
s 只包含字符 ‘*’ 和 ‘|’ 。
1 <= queries.length <= 10^5
queries[i].length == 2
0 <= lefti <= righti < s.length

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/plates-between-candles
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

二分+前缀和
我这里直接用的TreeMap

class Solution 
    public int[] platesBetweenCandles(String s, int[][] queries) 
        //我能想到的思路是用一个treemap,键是每个蜡烛的位置,值是右边到下一个蜡烛有多个盘子
        //然后查找的时候,先找到左边下标最近(右边)的蜡烛,然后找到右边下标最近(左边)的蜡烛
        //然后遍历treemap,把这个范围内的盘子都加起来
        //这样的话,因为是离线查询,极限是On方,应该会超时,不过先写一下吧
        //不不,应该统计蜡烛左边有多少盘子,这样就和前缀和一样了

        int l = s.length();
        TreeMap<Integer, Integer> map = new TreeMap<>();
        int idx = -1;
        int count = 0;

        //统计每个蜡烛右边有多少个盘子
        for(int i = 0; i < l; i++)
            char c = s.charAt(i);
            if(c == '|')
                idx = i;
                map.put(idx, count);
            else
                count++;
            
        

        //查询
        int n = queries.length;
        int[] res = new int[n];
        for(int i = 0; i < n; i++)
            int[] temp = queries[i];
            if(map.ceilingKey(temp[0]) == null)
                res[i] = 0;
                continue;
            
            int left = map.ceilingKey(temp[0]);
            if(map.floorKey(temp[1]) == null)
                res[i] = 0;
                continue;
            
            int right = map.floorKey(temp[1]);
            int ll = map.get(left);
            int rr = map.get(right);
            res[i] = rr - ll;
            if(left >= right)
                res[i] = 0;
        
        return res;
    

798. 得分最高的最小轮调

2022.3.9 每日一题

题目描述

给你一个数组 nums,我们可以将它按一个非负整数 k 进行轮调,这样可以使数组变为 [nums[k], nums[k + 1], … nums[nums.length - 1], nums[0], nums[1], …, nums[k-1]] 的形式。此后,任何值小于或等于其索引的项都可以记作一分。

例如,数组为 nums = [2,4,1,3,0],我们按 k = 2 进行轮调后,它将变成 [1,3,0,2,4]。这将记为 3 分,因为 1 > 0 [不计分]、3 > 1 [不计分]、0 <= 2 [计 1 分]、2 <= 3 [计 1 分],4 <= 4 [计 1 分]。
在所有可能的轮调中,返回我们所能得到的最高分数对应的轮调下标 k 。如果有多个答案,返回满足条件的最小的下标 k 。

示例 1:

输入:nums = [2,3,1,4,0]
输出:3
解释:
下面列出了每个 k 的得分:
k = 0, nums = [2,3,1,4,0], score 2
k = 1, nums = [3,1,4,0,2], score 3
k = 2, nums = [1,4,0,2,3], score 3
k = 3, nums = [4,0,2,3,1], score 4
k = 4, nums = [0,2,3,1,4], score 3
所以我们应当选择 k = 3,得分最高。

示例 2:

输入:nums = [1,3,0,2,4]
输出:0
解释:
nums 无论怎么变化总是有 3 分。
所以我们将选择最小的 k,即 0。

提示:

1 <= nums.length <= 10^5
0 <= nums[i] < nums.length

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/smallest-rotation-with-highest-score
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

能直接想到的就是超时的方法,统计每个位置可以移动的次数,然后找到使分数最高的次数

class Solution 
    public int bestRotation(int[] nums) 
        //就是旋转数组,然后看哪个数组分最高
        //统计每一个数,在哪几个位置是满足条件的,然后将需要移动多少次,放在一个set集合中
        //因为最多移动长度减1次,所以统计哪一次分最高,然后输出这个最高分

        //例如第一个例子,2可以向左移动1,2,3次,3移动2,3次,1需要向左移动1 3 4次,4需要4次,0需要0 1 2 3 4次
        //那么可以看到,移动3次是最优选择,因为有4分
    
        int l = nums.length;
        
        Map<Integer, List<int[]>> map = new HashMap<>();
        for(int i = 0; i < l; i++)
            int t = nums[i];
            List<int[]> list = new ArrayList<>();
            //如果t大于下标i,那么需要移动到t到length - 1的位置,也就是说,需要移动i+1到i+1+length-1-t次
            if(t > i)
                list.add(new int[](i + 1) % l, (i + l - t) % l);
            //如果小于等于的时候,分两段
            //例如示例1中的1,那么比它大的位置肯定是满足条件的,就和上面情况相同,比它小的位置,也就是t到i,也是满足条件的
            else
                if(i + 1 <= l - 1)
                    list.add(new int[](i + 1) % l, l - 1);
                list.add(new int[]0, i - t);
            
            /*
            System.out.println(list.get(0)[0] + "+++");
            System.out.println(list.get(0)[1]);
            if(list.size() > 1)
                System.out.println(list.get(1)[0] + "---");
                System.out.println(list.get(1)[1] + "--------");
            */
            map.put(i, list);
        

        int max = 0;
        int count = 0;
        //当移动这么多次时,几个满足条件
        for(int i = 0; i < l; i++)
            int m = 0;
            for(int t : map.keySet())
                List<int[]> list = map.get(t);
                int[] t1 = list.get(0);
                if(i >= t1[0] && i <= t1[1])
                    m++;
                else if(list.size() > 1)
                    int[] t2 = list.get(1);
                    if(i >= t2[0] && i <= t2[1])
                        m++;
                
            
            if(m > max)
                max = m;
                count = i;
            
        
        return count;

    

那么怎么做改进呢
我们通过一次遍历,得到了每个位置可以移动的次数范围
知道这个范围以后,可以通过差分数组来实现对次数的统计
具体来说,对于一个数x,如果它移动的范围(即可以得分)是[left,right]
那么利用差分数组的思想,先创建一个差分数组diff[],然后令diff[left] + 1,令diff[right + 1] - 1
意思是到left这个位置,分数开始加1,到right+1这个位置,分数开始-1
然后计算这个差分数组的前缀和,就可以得到向左移动几次,得到的分数最高了

class Solution 
    public int bestRotation(int[] nums) 
        //就是旋转数组,然后看哪个数组分最高
        //统计每一个数,在哪几个位置是满足条件的,然后将需要移动多少次,放在一个set集合中
        //因为最多移动长度减1次,所以统计哪一次分最高,然后输出这个最高分

        //例如第一个例子,2可以向左移动1,2,3次,3移动2,3次,1需要向左移动1 3 4次,4需要4次,0需要0 1 2 3 4次
        //那么可以看到,移动3次是最优选择,因为有4分
    
        int l = nums.length;
        
        Map<Integer, List<int[]>> map = new HashMap<>();
        for(int i = 0; i < l; i++)
            int t = nums[i];
            List<int[]> list = new ArrayList<>();
            //如果t大于下标i,那么需要移动到t到length - 1的位置,也就是说,需要移动i+1到i+1+length-1-t次
            if(t > i)
                list.add(new int[](i + 1) % l, (i + l - t) % l);
            //如果小于等于的时候,分两段
            //例如示例1中的1,那么比它大的位置肯定是满足条件的,就和上面情况相同,比它小的位置,也就是t到i,也是满足条件的
            else
                if(i + 1 <= l - 1)
                    list.add(new int[](i + 1) % l, l - 1);
                list.add(new int[]0, i - t);
            
            /*
            System.out.println(list.get(0)[0] + "+++");
            System.out.println(list.get(0)[1]);
            if(list.size() > 1)
                System.out.println(list.get(1)[0] + "---");
                System.out.println(list.get(1)[1] + "--------");
            */
            map.put(i, list);
        

        int[] diff = new int[l + 1];
        for(int i = 0; i < l; i++)
            List<int[]> list = map.get(i);
            int[] t1 = list.get(0);
            diff[t1[0]]++;
            diff[t1[1] + 1]--;
            if(list.size() > 1)
                int[] t2 = list.get(1);
                diff[t2[0]]++;
                diff[t2[1] + 1]--;
            
        

        int max = 0;
        int count = 0;
        int score = 0;
        //当移动这么多次时,几个满足条件
        for(int i = 0; i < l; i++)
            score += diff[i];
            if(score > max)
                max = score;
                count = i;
            
        
        return count;

    

官解将分成两段的情况也合并成一段了,其实不太好理解

以上是关于LeetCode 504. 七进制数 / 2055. 蜡烛之间的盘子 / 798. 得分最高的最小轮调(差分数组)的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 504 七进制数[循环] HERODING的LeetCode之路

504 Base 7 七进制数

LeetCode1035. 不相交的线 / 504. 七进制数 / 315. 计算右侧小于当前元素的个数 / 剑指 Offer 52. 两个链表的第一个公共节点

504. 七进制数

504. 七进制数

「 每日一练,快乐水题 」504. 七进制数