以赛促练-力扣第309场周赛反思

文章目录

        • T1.2399.检查相同字母间的距离
        • T2.2400.恰好移动k步到达某一位置的方法数目
        • T3.2401.最长优雅子数组
        • T4.2402.会议室III

本次周赛也暴露出来了很多问题,发现以前刷的题目太少或者还没有完全融会贯通,碰到一个新场景总是没法运用上来。当两个有点生疏的知识点一组合,就成为了我解不出的题目。比如说第三题,看到大家说滑动窗口和位运算,我其实都有去做过,但是两个知识点结合的新题一到手上,就发现解不出来了。

T1.2399.检查相同字母间的距离

dis数组中有三种可能的值,-1表示未出现过,由于如果出现那么必定出现两次,因此第一次出现做记录,第二次出现计算差值。

class Solution {
    public boolean checkDistances(String s, int[] distance) {
        int[]dis=new int[26];
        int len=s.length();
        Arrays.fill(dis,-1);
        for(int i=0;i<len;i++){
            char c=s.charAt(i);
            if(dis[c-'a']==-1) dis[c-'a']=i;
            else dis[c-'a']=i-dis[c-'a']-1;
        }
        for(int i=0;i<26;i++){
            if(dis[i]==-1) continue;
            if(dis[i]!=-1&&dis[i]!=distance[i]) return false;
        }
        return true;
    }
}
T2.2400.恰好移动k步到达某一位置的方法数目

当时只想着动态规划能做,但是始终不知道如何表示恰好走几步以及走的位置,还有当走出去又走回来怎么办,看朋友的题解才想到,开辟二维数组表示,每轮都暴力枚举所有的空间状态就可以了,某个位置的方法数目只和其左右两边有关。因此用二维动态规划,dp[i][j]代表最多走i步到下标j的方法数目。因为会往左右两边都走,因此出发点设置在中间。为了计算最终状态dp[k][1005+endPos-startPos],每轮的j都需要从开始的位置枚举到最多走i步,其状态只和左右两边状态有关。

class Solution {
    public int numberOfWays(int startPos, int endPos, int k) {
        int[][]dp=new int[k+1][2010];
        dp[0][1005]=1;
        int mod=(int)1e9+7;
        for(int i=1;i<=k;i++){
            for(int j=0;j<=i;j++){
                dp[i][1005+j]=(dp[i-1][1005+j-1]+dp[i-1][1005+j+1])%mod;
                dp[i][1005-j]=(dp[i-1][1005-j-1]+dp[i-1][1005-j+1])%mod;
            }
        }
        return dp[k][1005+endPos-startPos];
    }
}

动态规划可以做,同样记忆化搜索也可以做,递归函数中返回到某个位置x还剩余left步的情况下,能够有的方案数,将(x,left)进行记忆化。Python题解如下:

class Solution:
    def numberOfWays(self, startPos: int, endPos: int, k: int) -> int:
        MOD=10**9+7

        @cache
        # 返回到某个位置x还剩余left步的情况下,能够有的方案数
        def dfs(x:int, left:int) -> int:
            # 当没法到达终点的时候,包括left=0但是还没到终点
            if abs(x-endPos)>left: return 0
            # 在排除了上面的情况后,剩下的left=0一定是到达终点的
            if(left==0): return 1
            return (dfs(x-1,left-1)+dfs(x+1,left-1))%MOD
        return dfs(startPos,k)

用Java的HashMap实现记忆化搜索,在数据范围合适的情况下,对原来需要存储两个维度的信息能够转换为一个维度的信息,这题就需要存储当前位置信息和剩余步数信息

class Solution {
    int mod=(int)1e9+7;
    int endPos;
    HashMap<Integer,Integer> map;
    public int numberOfWays(int startPos, int _endPos, int k) {
        endPos=_endPos;
        map=new HashMap<>();
        return (int)dfs(startPos,k);
    }
    public long dfs(int x,int left){
        if(Math.abs(x-endPos)>left) return 0;
        if(left==0) return 1;
        //二维信息转一维
        //由于0<=left<=1000,因此可以让当前位置乘以一个大于1000的数再加上它
        int key=x*1005+left;
        if(map.containsKey(key)) return map.get(key);
        int ans= (int)(dfs(x-1,left-1)+dfs(x+1,left-1))%mod;
        map.put(key,ans);
        return ans;
    }
}
T3.2401.最长优雅子数组

滑动窗口+位运算。用一个数字窗口window维护整型32位上出现的数字,每次移动right只需要判断当前数字与窗口中的&以后,是否为0,如果为0则代表所有数字都散落在窗口的不同位置,如果不为0则需要收缩左边界。可以保证的是,如果当前的数字与窗口&只为0才加入,那么窗口中所有的数字互相&都是0.

class Solution {
    public int longestNiceSubarray(int[] nums) {
        int res=0;
        int len=nums.length;
        int window=0;
        int left=0;
        int right=0;
        while(right<len){
            int temp=window & nums[right];
            while(temp!=0){
                window ^= nums[left];
                temp = window & nums[right];
                left++;
            }
            window |= nums[right];
            res=Math.max(res,right-left+1);
            right++;
        }
        return res;
    }
}
T4.2402.会议室III

双堆模拟。“当会议室处于未占用状态时,将会优先提供给原开始时间更早的会议。”从这句话就可以知道,会议的安排只和会议的开始时间顺序相关,因此可以按照开始时间对会议升序处理。用一个小顶堆维护空闲会议室编号,这是为了每次能够取用最小编号的空闲会议室;用另一个小顶堆维护(已占用会议室的结束时间,已占用编号),在遍历所有会议的时候,每次将遍历的meetings[0]前结束的会议弹出,放入空闲会议室。接下来在判断当前会议加入哪个会议室的时候,如果有空会议室,则占用;没有可用的会议室,则等待,编程上反映就是弹出已占有会议室中结束时间最早的会议室(但仍然比当前meetings[0]要大),占用它并更新已占用会议室的结束时间的差值(相当于当前会议室延后了我等待的时间),将其加入占用会议室堆。

时间复杂度:O(MlogM+MlogN+N)。分别为会议排序时间,遍历会议调整堆中会议室,最后选出答案。


class Solution {
    public int mostBooked(int n, int[][] meetings) {
        Arrays.sort(meetings,(a,b)->a[0]-b[0]);
        int[]ans=new int[n];
        PriorityQueue<Integer> pqidle=new PriorityQueue<>();
        //存放(会议室结束时间,编号),优先选择结束时间最早的会议室,当结束时间相同,优先弹出编号小的
        PriorityQueue<Pair<Long,Integer>> pqusing=new PriorityQueue<Pair<Long,Integer>>((a,b)->Objects.equals(a.getKey(),b.getKey())?Integer.compare(a.getValue(),b.getValue()):Long.compare(a.getKey(),b.getKey()));
        for(int i=0;i<n;i++) pqidle.offer(i);
        int len=meetings.length;
        for(int i=0;i<len;i++){
            long start=meetings[i][0];
            long end=meetings[i][1];
            //将start以前结束的会议室放出
            while(!pqusing.isEmpty()&&start>=pqusing.peek().getKey()){
                Pair<Long,Integer> top=pqusing.poll();
                pqidle.offer(top.getValue());
            }

            int id;
            //如果没有空闲会议室
            if(pqidle.size()==0){
                //等待top.getKey()-start的时间空出来并占用
                Pair<Long,Integer>top=pqusing.poll();
                end+=top.getKey()-start;
                id=top.getValue();
                pqusing.offer(new Pair<Long,Integer>(end,id));
            }else{
                int idletop=pqidle.poll();
                id=idletop;
                pqusing.offer(new Pair<Long,Integer>(end,idletop));
            }
            ans[id]+=1;
        }
        int res=0;
        for(int i=0;i<ans.length;i++){
            if(ans[i]>ans[res]) res=i;
        }
        return res;
    }
}

你可能感兴趣的:(算法人生,leetcode,深度优先,算法)