力扣-第52场双周赛

四个题目难度分别为简单、中等、中等、困难;解法不一定最优,能AC,欢迎交流。

 

5742.将句子排序

题意:有一个类似“is2 sentence4 This1 a3”,长度不超过200的字符串,单词后面跟着数字,单词用空格隔开,要求按数字升序重新拼接字符串并去掉数字,变成“This is a sentence”。

思路:单词数不超过9,只需要1位来表示单词应该存在的位置;按空格分割字符串,提取数字放到新单词数组的指定位置,最后拼接时去掉数字。

    /** 5742.将句子排序
     * 按空格分割字符串,提取数字放到新单词数组的指定位置,最后拼接时去掉数字。
     */
    public String sortSentence(String s) {
        //分割
        String[] a=s.split(" ");
        int n=a.length;
        String[] ans=new String[n];
        //每个字符串的最后一个是数字,落到新字符串中
        for(int i=0;i<n;i++){
            int idx=a[i].charAt(a[i].length()-1)-'0';
            ans[idx-1]=a[i];
        }
        //拼接排序后的字符串,注意中间的空格数比单词数少1
        String res="";
        for(int i=0;i<n;i++){
            if(i==0)
                res=res+ans[i].substring(0,ans[i].length()-1);
            else
                res=res+" "+ans[i].substring(0,ans[i].length()-1);
        }
        return res;
    }

 

 


 5743. 增长的内存泄露

题意:有两个内存条,剩余内存分别为m1和m2;现有一个程序在消耗内存,第i秒消耗i内存;每秒钟,如果两个内存条剩余内存大者被程序消耗,相同则消耗第一个内存条;两个内存条的剩余内存都不满足程序消耗则程序奔溃;问程序何时奔溃;

思路:模拟程序运行的消耗,按条件减少内存条的剩余内存,不满足条件时返回;

    /** 5743. 增长的内存泄露
     * 模拟程序运行的消耗,按条件减少内存条的剩余内存,不满足条件时返回;
     */
    public static int[] memLeak(int m1, int m2) {
        int[] res=new int[3];
        int t=1;
        //需要占用的内存比两个内存条都大,则程序奔溃
        while(!(t>m1 && t>m2)){
            if(m1>=m2)
                m1-=t;
            else
                m2-=t;
            t++;
        }
        res[0]=t;
        res[1]=m1;
        res[2]=m2;
        return res;
    }

 


 5744. 旋转盒子

题意:现有m*n的字符矩阵,字符代码石头、固定物、空位置;将矩阵顺时针旋转90°,石头如果悬空会因为惯性落下;求最后的字符矩阵;

思路:

1.不管三七二十一,先顺时针旋转90°;

2.对于每一列,从下往上遍历,找到两个固定物之间的信息,固定物包含地底和天花板;

3.计算上下固定物之间的石头数,从固定物下方堆砌石头,剩余的空间用空位置填充;

4.注意点:地底和天花板作为固定物的表示

时空复杂度都为(n*m)

    /** 5744. 旋转盒子
     *
     */
    public char[][] rotateTheBox(char[][] box) {
        int n=box.length,m=box[0].length;
        char[][] res=new char[m][n];
        //正常旋转90
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++)
                res[j][n-1-i]=box[i][j];
        }
        n=res.length;
        m=res[0].length;
        //石头落下去,每一列从下往上,分为4种情况[地底,障碍物][地底,天上][障碍物,障碍物][障碍物,天上]
        for(int j=0;j<m;j++){
            //up表示上方固定物,down表示下方固定物,初始化为天花板和地底,num表示(up,down)之间的石头数
            int up=-1,down=n,num=0;
            for(int i=n-1;i>=0;i--){
                if(res[i][j]=='#'){//石头
                    num++;
                }else if(res[i][j]=='*'){//固定物
                    if(num==0){
                        //由于是从下往上,所以固定物先确定下方
                        down=i;
                    }else {
                        //上下固定物区间确定了,开始石头掉落的过程模拟
                        up=i;
                        change(res,up,down,num,j);
                        //清空石头数,并更新下方固定物位置
                        num=0;
                        down=i;
                    }
                }
            }
            //上方固定物为天花板
            if(num>0){
                up=-1;
                change(res,up,down,num,j);
            }
        }
        return res;
    }
    //j列中(up,down)之间有num块石头,从下往上堆,剩余空间用空位置填充
    public static void change(char[][] res,int up,int down,int num,int j){
        for(int i=down-1;i>up;i--){
            if(num>0){
                res[i][j]='#';
                num--;
            }else{
                res[i][j]='.';
            }
        }
    }

 

 


 5212. 向下取整数对和

题意:有长度为100000的数组,返回对所有下标0<=i,j<n的floor(a[i]/a[j])结果之和,答案对1e9+7求模;floor()函数返回整数部分;

样例:

输入[2,5,9] 输出10

floor(2 / 5) = floor(2 / 9) = floor(5 / 9) = 0 floor(2 / 2) = floor(5 / 5) = floor(9 / 9) = 1 floor(5 / 2) = 2 floor(9 / 2) = 4 floor(9 / 5) = 1

(0+1+2+4+1)%(1000000007)=10;

思路:

1.确定方向

对于floor(x/y)函数,假设y一定,则有多个x使得floor(x/y)函数值相同;例如y=9,x∈[9,17]时函数值都是1,x∈[18,26]时函数值都是2, x∈[27,35]时函数值都是3;显然需要进行区间计数,线段树、树状数组、前缀和是常规操作,这里选择前缀和即可;

2.看数据范围

数组长度和元素大小都是105,可以直接开大数组num;

先计数:num[i]表示元素i的个数;

后变成前缀和数组:num[i]表示[0,i]的元素个数总和,通过num[i]-num[i-1]则表示元素i的个数;

这里最大元素maxx作为数组长度,不需要每次都开满数组长度为105,节省空间;

3.倍数关系

对于元素i,每次找一段区间内的元素总个数,计算函数值之和;

倍数区间形如[i,i×2-1]、[i×2,i×3-1]、[i×3,i×4-1] ... [i×(j-1),i×j-1];

倍数×区间内的元素总个数 = 元素i在该段区间的函数值总和;

元素i的个数×倍数×区间内的元素总个数 = 所有i在该段区间的函数值总和;

再对多段区间进行累加即可;

4.越界情况

极限数据105×105=1010会导致int溢出,计算过程用long变量,最后转int;

当i*j>maxx时直接使用i*j作为数组下标会使得数组越界;

    /** 5212. 向下取整数对和
     * 求所有floor(a[i] / a[j])之和
     * 前缀和
     */
    public int sumOfFlooredPairs(int[] a) {
        long res=0,p=1000000007;
        int n=a.length,maxx=0;
        for(int i=0;i<n;i++){
            maxx=Math.max(maxx,a[i]);
        }
        int[] num=new int[maxx+1];
        //计数
        for(int i=0;i<n;i++)
            num[ a[i] ]++;
        //前缀和
        for(int i=1;i<=maxx;i++)
            num[i]+=num[i-1];
        for(int i=1;i<=maxx;i++){
            //x表示数字i的个数
            long x=num[i]-num[i-1];
            if(x==0)
                continue;
            //找区间,[i,i*2-1]、[i*2,i*3-1]、[i*3,i*4-1],每次+i
            for(int j=i;j<=maxx;j=j+i){
                //y表示区间的个数,如果j+i-1>maxx则取maxx即可,防止数组下标越界
                long y=num[Math.min(j+i-1,maxx)]-num[j-1];
                res=(res+(j/i)*y*x)%p;
            }
        }
        return (int)res;
    }

 

posted @ 2021-05-17 01:44  守林鸟  阅读(120)  评论(0编辑  收藏  举报