[Leetcode Weekly Contest]285

链接:LeetCode

[Leetcode]2210. 统计数组中峰和谷的数量

给你一个下标从 0 开始的整数数组 nums 。如果两侧距 i 最近的不相等邻居的值均小于 nums[i] ,则下标 i 是 nums 中,某个峰的一部分。类似地,如果两侧距 i 最近的不相等邻居的值均大于 nums[i] ,则下标 i 是 nums 中某个谷的一部分。对于相邻下标 i 和 j ,如果 nums[i] == nums[j] , 则认为这两下标属于 同一个 峰或谷。

注意,要使某个下标所做峰或谷的一部分,那么它左右两侧必须 都 存在不相等邻居。

返回 nums 中峰和谷的数量。

遍历即可。

class Solution {
    public int countHillValley(int[] nums) {
        int res = 0;
        Boolean increase = null;
        int pre = nums[0];
        for(var num:nums) {
            if(num>pre) {
                increase = true;
                break;
            }
            else if(num<pre) {
                increase = false;
                break;
            }
        }
        if(increase == null) return res;

        for(var num:nums) {
            if(num < pre) {
                if(increase) res += 1;
                increase = false;
            }
            else if(num>pre) {
                if(!increase) res += 1;
                increase = true;
            }
            pre = num;
        }
        return res;
    }
}

[Leetcode]2211. 统计道路上的碰撞次数

在一条无限长的公路上有 n 辆汽车正在行驶。汽车按从左到右的顺序按从 0 到 n - 1 编号,每辆车都在一个 独特的 位置。

给你一个下标从 0 开始的字符串 directions ,长度为 n 。directions[i] 可以是 'L'、'R' 或 'S' 分别表示第 i 辆车是向 左 、向 右 或者 停留 在当前位置。每辆车移动时 速度相同 。

碰撞次数可以按下述方式计算:

当两辆移动方向 相反 的车相撞时,碰撞次数加 2 。
当一辆移动的车和一辆静止的车相撞时,碰撞次数加 1 。
碰撞发生后,涉及的车辆将无法继续移动并停留在碰撞位置。除此之外,汽车不能改变它们的状态或移动方向。

返回在这条道路上发生的 碰撞总次数 。

逻辑题,去掉往左右两边开的车之后,剩下的车必然会相撞。也可以通过栈模拟,来实现。

class Solution {
    public int countCollisions(String directions) {
        int start = 0, end = directions.length()-1;
        while(start<=end) {
            if(directions.charAt(start) != 'L') break;
            start ++;
        }
        while(start<=end) {
            if(directions.charAt(end) != 'R') break;
            end --;
        }
        int count = 0;
        for(var ch:directions.toCharArray()) {
            if(ch == 'S') count ++;
        }
        return end-start+1 - count;
    }
}

[Leetcode]2212. 射箭比赛中的最大得分

Alice 和 Bob 是一场射箭比赛中的对手。比赛规则如下:

Alice 先射 numArrows 支箭,然后 Bob 也射 numArrows 支箭。
分数按下述规则计算:
箭靶有若干整数计分区域,范围从 0 到 11 (含 0 和 11)。
箭靶上每个区域都对应一个得分 k(范围是 0 到 11),Alice 和 Bob 分别在得分 k 区域射中 ak 和 bk 支箭。如果 ak >= bk ,那么 Alice 得 k 分。如果 ak < bk ,则 Bob 得 k 分
如果 ak == bk == 0 ,那么无人得到 k 分。
例如,Alice 和 Bob 都向计分为 11 的区域射 2 支箭,那么 Alice 得 11 分。如果 Alice 向计分为 11 的区域射 0 支箭,但 Bob 向同一个区域射 2 支箭,那么 Bob 得 11 分。

给你整数 numArrows 和一个长度为 12 的整数数组 aliceArrows ,该数组表示 Alice 射中 0 到 11 每个计分区域的箭数量。现在,Bob 想要尽可能 最大化 他所能获得的总分。

返回数组 bobArrows ,该数组表示 Bob 射中 0 到 11 每个 计分区域的箭数量。且 bobArrows 的总和应当等于 numArrows 。

如果存在多种方法都可以使 Bob 获得最大总分,返回其中 任意一种 即可。

二进制枚举或者动态规划。 考虑到这道题numArrows可能会很大,而枚举只有\(2^{12}-1\)种可能,二进制枚举算法更优。

class Solution {

    // 二进制枚举
    public int[] maximumBobPoints(int numArrows, int[] aliceArrows) {
        int maxScore = 0, maxNeed = 0;
        int plan = 0;
        int[] res = new int[12];
        for(int i=1;i<Math.pow(2, 13);++i) {
            int need = 0, score = 0;
            for(int j=0;j<12;++j) {
                if((i & (1<<j))!=0){
                    need += aliceArrows[j]+1;
                    score += j;
                }
            }
            if(need <= numArrows && score > maxScore) {
                maxScore = score;
                maxNeed = need;
                plan = i;
            }
        }

        for(int i=0;i<12;++i) {
            if((plan & (1<<i))!=0) res[i] = aliceArrows[i]+1;
        }
        res[0] += numArrows - maxNeed;
        return res;
    }

    // 动态规划
    public int[] maximumBobPoints(int numArrows, int[] aliceArrows) {
        int[][] dp = new int[12][numArrows+1];
        int[] res = new int[12];
        for(int i=1;i<12;++i) {
            for(int num=0;num<numArrows+1;++num) {
                if(num>aliceArrows[i]) dp[i][num] = Math.max(dp[i-1][num], dp[i-1][num-aliceArrows[i]-1]+i);
                else dp[i][num] = dp[i-1][num];
            }
        }

        for(int i=11;i>=1;--i) {
            if(dp[i][numArrows] > dp[i-1][numArrows]) {
                res[i]  = aliceArrows[i]+1;
                numArrows -= aliceArrows[i]+1;
            }
        }
        res[0] += numArrows;
        return res;
    }
}

[Leetcode]2213. 由单个字符重复的最长子字符串

给你一个下标从 0 开始的字符串 s 。另给你一个下标从 0 开始、长度为 k 的字符串 queryCharacters ,一个下标从 0 开始、长度也是 k 的整数 下标 数组 queryIndices ,这两个都用来描述 k 个查询。
第 i 个查询会将 s 中位于下标 queryIndices[i] 的字符更新为 queryCharacters[i] 。
返回一个长度为 k 的数组 lengths ,其中 lengths[i] 是在执行第 i 个查询 之后 s 中仅由 单个字符重复 组成的 最长子字符串 的 长度 。

一道经典的线段树应用题。使用线段树求解,我们唯一需要考虑的是:在 Node 中维护些什么信息?
对于线段树的节点信息设计,通常会包含基本的左右端点 l、r 以及查询目标值 val ,然后再考虑维护 val 还需要一些什么辅助信息。
对于本题,我们还需要额外维护 prefix 和 suffix,分别代表「当前区间 [l, r][l,r] 内前缀相同字符连续段的最大长度」和「当前区间 [l, r][l,r] 内后缀相同字符连续段的最大长度」。

class Solution {
    class Node {
        int l, r, prefix, suffix, val;
        Node(int _l, int _r) {
            l = _l; r = _r;
            prefix = suffix = val = 1;
        }
    }
    char[] cs;
    Node[] tr;
    void build(int u, int l, int r) {
        tr[u] = new Node(l, r);
        if (l == r) return ;
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
    }
    void update(int u, int x, char c) {
        if (tr[u].l == x && tr[u].r == x) {
            cs[x - 1] = c;
            return ;
        }
        int mid = tr[u].l + tr[u].r >> 1;
        if (x <= mid) update(u << 1, x, c);
        else update(u << 1 | 1, x, c);
        pushup(u);
    }
    int query(int u, int l, int r) {
        if (l <= tr[u].l && tr[u].r <= r) return tr[u].val;
        int ans = 0;
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) ans = query(u << 1, l, r);
        if (r > mid) ans = Math.max(ans, query(u << 1 | 1, l, r));
        return ans;
    }
    void pushup(int u) {
        Node left = tr[u << 1], right = tr[u << 1 | 1];
        int aLen = left.r - left.l + 1, bLen = right.r - right.l + 1;
        char ac = cs[left.r - 1], bc = cs[right.l - 1];
        tr[u].prefix = left.prefix; tr[u].suffix = right.suffix;
        tr[u].val = Math.max(left.val, right.val);
        if (ac == bc) {
            if (left.prefix == aLen) tr[u].prefix = aLen + right.prefix;
            if (right.prefix == bLen) tr[u].suffix = bLen + left.suffix;
            tr[u].val = Math.max(tr[u].val, left.suffix + right.prefix);
        }
    }
    public int[] longestRepeating(String s, String queryCharacters, int[] queryIndices) {
        cs = s.toCharArray();
        int n = cs.length, m = queryCharacters.length();
        tr = new Node[n * 4];
        build(1, 1, n);
        for (int i = 0; i < n; i++) update(1, i + 1, cs[i]);
        int[] ans = new int[m];
        for (int i = 0; i < m; i++) {
            update(1, queryIndices[i] + 1, queryCharacters.charAt(i));
            ans[i] = query(1, 1, n);
        }
        return ans;
    }
}

Leetcode

posted @ 2022-05-18 20:42  Jamest  阅读(18)  评论(0编辑  收藏  举报