第309场周赛

检查相同字母间的距离

解题思路

题目比较长,读起来有点费劲, 意思就是说每个在s中出现的字母, 都会出现两次, 如果这两次之间的字母个数和distance数组中记录的都一致的话,就返回真,否则为假.

首先要把每个字母ch之间出现的其他字母个数记录在一个map 中, 为了方便映射到distance数组, key 就为ch-‘a’
为了节省内存, 这个map的功能可以服用, 当字母第一次出现时,用map 记录下它的下标,当字母第二次出现时,就可以利用第一次出现的 下标计算出两次出现之间有多少其他字母

最后与distance数组比较

代码实现

class Solution {
    public boolean checkDistances(String s, int[] distance) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int i = 0;i<s.length();i++){
            int num = s.charAt(i)  - 'a';
            if(!map.containsKey(num)){
                map.put(num,i);
            }else{
                map.put(num,i - map.get(num)-1);
            }
        }
        for(Map.Entry<Integer,Integer> entry:map.entrySet()){
            if(distance[entry.getKey()] != entry.getValue()){
                return false;
            }
        }
        return true;
    }
}

恰好移动 k 步到达某一位置的方法数目

解题思路

假设规定从左到右走为正方向,从右往左走为负方向, 假设从开始位置走到结束位置有a步走了正方向, 那么在负方向就走了 k-a步, 开始位置和结束之间的步数为 d = |endPos - endPos|, 那么 a - (k-a) = d, 那么a = (k+d)/2, 那么题目的意思就转变为k步中选(k+d)/2步走正方向的选择数.使用递推的方式求组合数

代码

class Solution {
    private static int MOD = 1000000007;
    public int numberOfWays(int startPos, int endPos, int k) {
        int d = Math.abs(endPos - startPos);
        
        if(k<d || (k+d)%2 == 1){
            return 0;
        }
        // i 步当中 j 步走正方向的走法
        int[][] dp = new int[k+1][k+1];
        for(int i = 0;i<=k;i++){
            dp[i][0] = 1;
            for(int j = 1;j<=i;j++){
                dp[i][j] = (dp[i-1][j] + dp[i-1][j-1]) % MOD;
            }
        }
        return dp[k][(d+k)/2];
    }
}

可能这道题还是过于抽象,我们来看另一道相似的
不同的路径

解题思路

一种比较简单直白的思路
我们使用dp[i][j] 记录机器人走到[i,j]这个位置的路径数,我们知道它要走到[i,j], 就要么从[i,j-1]的位置往右一步,要么从[i-1,j]的位置往下走一步, 所以,它走到[i,j]的路径数等于[i-1,j]的路径数加上[i-1,j]的路径数
另外,第一行 和 第一列 的路径数都只能是1, 因此有如下代码

class Solution {
    public int uniquePaths(int m, int n) {
        int[][]dp = new int[m][n];
        Arrays.fill(dp[0],1);
        for(int i = 0;i<m;i++){
            dp[i][0] = 1;
        }
        for(int i = 1;i<m;i++){
            for(int j = 1;j<n;j++){
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
        return dp[m-1][n-1];

    }
}

这道还可以从另外一种角度来思考, 机器人走[0,0] 走到[m,n] 一共需要走m+n-2步, 它一共往下走了m-1步, 那么这道题就可以变为m+n-2步中选择m-1步往下走的选择数,因此也可以这样写.
这种做法就和 恰好移动 k 步到达某一位置的方法数目 的解题思路一致

class Solution {
    public int uniquePaths(int m, int n) {
        
        int[][]dp = new int[m+n-1][m];
        for(int i = 0;i<m+n-1;i++){
            dp[i][0] = 1;
            for(int j = 1;j<=Math.min(i,m-1);j++){
                dp[i][j] = dp[i-1][j] + dp[i-1][j-1];
            }
        }
        return dp[m+n-2][m-1];

    }
}

#最长优雅子数组

解题思路

利用dp[i]记录以nums[i]为结尾的最长优雅子数组的长度, 因为子数组连续的, dp[i]的最大长度只能是dp[i-1]+1,此时nums[i] 前dp[i-1]个元素逻辑与的结果都是0, 只能它的长度是多少,看它能连续跟多少个之前的元素逻辑与的结果为0

代码实现

class Solution {
    public int longestNiceSubarray(int[] nums) {
        int max = 1;
        int[] dp = new int[nums.length];
        Arrays.fill(dp,1);
        for(int i = 1;i<nums.length;i++){
            int cnt = 1;
            // 往前遍历,只需要遍历到前dp[i-1]个元素即可, 
            for(int j = 1;j<=dp[i-1];j++){
                if((nums[i]&nums[i-j]) == 0){
                    cnt++;
                }else{
                    break;
                }
            }
            dp[i] = cnt;
            max = Math.max(max,cnt);
        }
        return max;

    }
}

从上面的过程可以看到dp数组其实每次都只是利用了前一个元素而已,因此可以只使用一个变量来表示,从而将空间复杂度从O(n)优化到O(1)

class Solution {
    public int longestNiceSubarray(int[] nums) {
        int max = 1;
        int pre = 1;
        for(int i = 1;i<nums.length;i++){
            int cnt = 1;
            for(int j = 1;j<=pre;j++){
                if((nums[i]&nums[i-j]) == 0){
                    cnt++;
                }else{
                    break;
                }
            }
            pre = cnt;
            max = Math.max(max,cnt);
        }
        return max;

    }
}

会议室III

解题思路

代码

class Solution {
    public int mostBooked(int n, int[][] meetings) {

        // [0]为会议结束时间, [1]为会议室编号
        PriorityQueue<int[]> queue = new PriorityQueue<>((a,b)->a[0]==b[0]?a[1]-b[1]:a[0]-b[0]);
        // 初始化会议的结束时间为0
        for(int i = 0;i<n;i++){
            queue.offer(new int[]{0,i});
        }
        // 记录每个会议室开会议的次数
        int[]room = new int[n];
        Arrays.sort(meetings,(a,b)->a[0]-b[0]);

        for(int[]m:meetings){
            //将已经结束的会议室初始化为可用
            Set<Integer> set = new HashSet<>();
            while(!queue.isEmpty() && queue.peek()[0]<=m[0]){
                set.add(queue.poll()[1]);
            }
            for(int r:set){
                queue.offer(new int[]{0,r});
            }
            int[] q = queue.poll();
            room[q[1]]++;
            int d = m[1]-m[0];
            queue.offer(new int[]{Math.max(q[0],m[0])+d,q[1]});
        }
        int max = 0;
        int ans  = 0;
        for(int i = 0;i<n;i++){
            if(room[i]>max){
                max = room[i];
                ans = i;
            }
        }
        return ans;

    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值