1. 程式人生 > 其它 >[Leetcode Weekly Contest]285

[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