[Leetcode Weekly Contest]261

链接:LeetCode

[Leetcode]2027. 转换字符串的最少操作次数

给你一个字符串 s ,由 n 个字符组成,每个字符不是 'X' 就是 'O' 。
一次 操作 定义为从 s 中选出 三个连续字符 并将选中的每个字符都转换为 'O' 。注意,如果字符已经是 'O' ,只需要保持 不变 。
返回将 s 中所有字符均转换为 'O' 需要执行的 最少 操作次数。

遍历即可。

class Solution {
    public int minimumMoves(String s) {
        int i = 0, n = s.length();
        int res = 0;
        while(i<n) {
            if(s.charAt(i) == 'X') {
                res ++;
                i += 3;
            }
            else {
                i ++;
            }
        }
        return res;
    }
}

[Leetcode]2028. 找出缺失的观测数据

现有一份 n + m 次投掷单个 六面 骰子的观测数据,骰子的每个面从 1 到 6 编号。观测数据中缺失了 n 份,你手上只拿到剩余 m 次投掷的数据。幸好你有之前计算过的这 n + m 次投掷数据的 平均值 。
给你一个长度为 m 的整数数组 rolls ,其中 rolls[i] 是第 i 次观测的值。同时给你两个整数 mean 和 n 。
返回一个长度为 n 的数组,包含所有缺失的观测数据,且满足这 n + m 次投掷的 平均值 是 mean 。如果存在多组符合要求的答案,只需要返回其中任意一组即可。如果不存在答案,返回一个空数组。
k 个数字的 平均值 为这些数字求和后再除以 k 。
注意 mean 是一个整数,所以 n + m 次投掷的总和需要被 n + m 整除。

mean * (m + n) 求总数。
sum(rolls) 求已经有的数量。
sum = mean * (m+n) - sum(rolls) 剩下需要补充的平均分配就行。< N * 1 或者 > N * 6 则不可能有合法分配。

class Solution {
    public int[] missingRolls(int[] rolls, int mean, int n) {
        int m = rolls.length, sum_ = 0;
        for(var roll:rolls) {
            sum_ += roll;
        }
        int rest = (m+n) * mean - sum_;
        if(rest < n || rest > 6 * n) {
            return new int[0];
        }
        int dev = rest / n;
        int restNum = rest % n;
        int[] res = new int[n];
        Arrays.fill(res,dev);
        for(int i=0;i<restNum;++i) {
            res[i] += 1;
        }
        return res;
    }
}

[Leetcode]2029. 石子游戏 IX

Alice 和 Bob 再次设计了一款新的石子游戏。现有一行 n 个石子,每个石子都有一个关联的数字表示它的价值。给你一个整数数组 stones ,其中 stones[i] 是第 i 个石子的价值。
Alice 和 Bob 轮流进行自己的回合,Alice 先手。每一回合,玩家需要从 stones 中移除任一石子。
如果玩家移除石子后,导致 所有已移除石子 的价值 总和 可以被 3 整除,那么该玩家就 输掉游戏 。
如果不满足上一条,且移除后没有任何剩余的石子,那么 Bob 将会直接获胜(即便是在 Alice 的回合)。
假设两位玩家均采用 最佳 决策。如果 Alice 获胜,返回 true ;如果 Bob 获胜,返回 false 。
给你一个字符串 word ,如果 word 可以被放入 board 中,请你返回 true ,否则请返回 false 。

由于我们只关心总和能否被 33 整除,我们可以将 \(\textit{stones}\) 按照模 3 的结果分为 3 组,即 0、1 和 2。
根据题意,第一回合不能移除 0,否则直接输掉游戏,因此第一回合只能移除 1 或者 2。我们可以枚举这两种情况,如果其中一种可以让 Alice 获胜就返回 \(\texttt{true}\),否则返回 \(\texttt{false}\)
下面以第一回合移除 1 来说明。在不考虑移除 0 的前提下,后面的移除由于要满足总和不能被 3 整除,因此移除的石子是固定的,整体构成一个 \(112121212\dots\) 循环的序列。
对于 0,由于移除之后不会改变总和模 33 的结果,因此不会改变后续 11 和 22 的移除顺序,所以我们可以在序列的任意非开头位置插入 0。两人为了不让自己输掉,必然会按照上述序列进行,直到没有石子,或某一方只能移除导致总和被 3 整除的石子时分出胜负。因此我们需要求出让总和不能被 3 整除的最大的回合数,这相当于 \(112121212\dots\) 序列的最长长度,加上 0 的个数。
若该回合数为奇数,且还有剩余石子,那么下一回合要轮到 Bob 移除石子,且他只能移除一枚让总和被 3 整除的石子,于是 Alice 获胜;否则 Bob 获胜。
对于第一回合移除 2 的情况,同样会构成一个 \(221212121\dots\) 循环的序列,做法同上。

class Solution {
    public boolean stoneGameIX(int[] stones) {
        HashMap<Integer,Integer> hm = new HashMap<>();
        for(var stone:stones)
        {
            var key =stone % 3;
            hm.put(key,hm.getOrDefault(key,0)+1);
        }
        hm.put(0,hm.getOrDefault(0,0)%2);
        int n = 0;
        for(var val:hm.values()) n += val;
        boolean res = getRes(n,hm,1) || getRes(n,hm,2);
        return res;
    }

    public boolean getRes(int n, HashMap hm, int first) {
        var dic = (HashMap<Integer,Integer>)hm.clone();
        if(dic.getOrDefault(first,0) == 0) {
            return false;
        }
        dic.put(first,dic.get(first)-1);
        var cur = first;
        for(int i=0;i<n-1;++i) {
            int need_to_remove;
            if(dic.getOrDefault(0,0)>0) {
                need_to_remove = 0;
            }
            else if(dic.getOrDefault(cur,0)>0) {
                need_to_remove = cur;
            }
            else {
                return (i & 1) == 0;
            }
            cur  = (cur + need_to_remove) % 3;
            dic.put(need_to_remove,dic.getOrDefault(need_to_remove,0)-1);
        }
        return false;
    }
}

[Leetcode]2030. 含特定字母的最小子序列

给你一个字符串 s ,一个整数 k ,一个字母 letter 以及另一个整数 repetition 。
返回 s 中长度为 k 且 字典序最小 的子序列,该子序列同时应满足字母 letter 出现 至少 repetition 次。生成的测试用例满足 letter 在 s 中出现 至少 repetition 次。
子序列 是由原字符串删除一些(或不删除)字符且不改变剩余字符顺序得到的剩余字符串。
字符串 a 字典序比字符串 b 小的定义为:在 a 和 b 出现不同字符的第一个位置上,字符串 a 的字符在字母表中的顺序早于字符串 b 的字符。

单调栈。首先:这题是要求一个子序列res,以下是这个res需要满足的条件:

  • 从原序列中删去n-k个元素,之后得到res
  • res中有至少repetition 个letter
  • res是满足上面两点要求的集合中字典序最小的。

如果是只满足1,3就是一个单调栈的基本做法。从前往后扫描(枚举),只要\(s[i]<res.back()\),且还能删(pop),就继续res.pop_back().
然后再来看看2怎么满足,我们是从前往后枚举的,因此不能删除到后面letter不够的情况,因此用cnt来维护后面还剩下多少个letter,不够就不能删,这样就会出现我们最后删除的个数< n - k ,但是字典序还是最小的,因此我们可以将最后的res长度删减为k,并补上letter在末尾。

class Solution {
    public String smallestSubsequence(String s, int k, char letter, int repetition) {
        int n = s.length();
        int[] suffix_cnt = new int[n];
        int acc = 0;
        for(int i=n-1;i>=0;--i) {
            if(s.charAt(i)==letter) acc ++;
            suffix_cnt[i] = acc;
        }
        Deque <Character> stk = new LinkedList<>();
        int x = 0;
        for(int i=0;i<n;++i) {
            int y = x;
            while((stk.size()>0) && (stk.size()+(n-i-1)>=k) && (s.charAt(i) < stk.peek())) {
                if(stk.peek() == letter) y--;
                if(y+suffix_cnt[i]<repetition) break;
                stk.pop();
                x = y;
            }
            if(stk.size()<k) {
                if(s.charAt(i) == letter || k - stk.size() > repetition - x) {
                    stk.push(s.charAt(i));
                    if (s.charAt(i) == letter) x++;
                }
            }
        }
        StringBuilder SB = new StringBuilder();
        while (!stk.isEmpty())
        {
            SB.append(stk.pollLast());
        }
        return SB.toString();
    }
}

Leetcode

posted @ 2021-10-08 21:25  Jamest  阅读(44)  评论(0编辑  收藏  举报