LeetCode Weekly Contest 185

5388. 重新格式化字符串

给你一个混合了数字和字母的字符串 s,其中的字母均为小写英文字母。

请你将该字符串重新格式化,使得任意两个相邻字符的类型都不同。也就是说,字母后面应该跟着数字,而数字后面应该跟着字母。

请你返回 重新格式化后 的字符串;如果无法按要求重新格式化,则返回一个 空字符串 。

示例 1:

输入:s = “a0b1c2”
输出:“0a1b2c”
解释:“0a1b2c” 中任意两个相邻字符的类型都不同。 “a0b1c2”, “0a1b2c”, “0c2a1b” 也是满足题目要求的答案。
示例 2:

输入:s = “leetcode”
输出:""
解释:“leetcode” 中只有字母,所以无法满足重新格式化的条件。
示例 3:

输入:s = “1229857369”
输出:""
解释:“1229857369” 中只有数字,所以无法满足重新格式化的条件。
示例 4:

输入:s = “covid2019”
输出:“c2o0v1i9d”
示例 5:

输入:s = “ab123”
输出:“1a2b3”

提示:

1 <= s.length <= 500
s 仅由小写英文字母和/或数字组成。

代码

class Solution:
    def reformat(self, s: str) -> str:
        ret = ""
        chs = list()
        nums = list()
        for ch in s:
            if ch >= '0' and ch <= '9':
                nums.append(ch)
            else:
                chs.append(ch)
        lchs = len(chs)
        lnums = len(nums)
        if lchs == lnums:
            for i in range(lchs):
                ret += chs[i] + nums[i]
        elif lchs == lnums + 1:
            for i in range(lnums):
                ret += chs[i] + nums[i]
            ret += chs[-1]
        elif lnums == lchs + 1:
            for i in range(lchs):
                ret += nums[i] + chs[i]
            ret += nums[-1]
        return ret

5389. 点菜展示表

给你一个数组 orders,表示客户在餐厅中完成的订单,确切地说, orders[i]=[customerNamei,tableNumberi,foodItemi] ,其中 customerNamei 是客户的姓名,tableNumberi 是客户所在餐桌的桌号,而 foodItemi 是客户点的餐品名称。

请你返回该餐厅的 点菜展示表 。在这张表中,表中第一行为标题,其第一列为餐桌桌号 “Table” ,后面每一列都是按字母顺序排列的餐品名称。接下来每一行中的项则表示每张餐桌订购的相应餐品数量,第一列应当填对应的桌号,后面依次填写下单的餐品数量。

注意:客户姓名不是点菜展示表的一部分。此外,表中的数据行应该按餐桌桌号升序排列。

示例 1:

输入:orders = [[“David”,“3”,“Ceviche”],[“Corina”,“10”,“Beef Burrito”],[“David”,“3”,“Fried Chicken”],[“Carla”,“5”,“Water”],[“Carla”,“5”,“Ceviche”],[“Rous”,“3”,“Ceviche”]]
输出:[[“Table”,“Beef Burrito”,“Ceviche”,“Fried Chicken”,“Water”],[“3”,“0”,“2”,“1”,“0”],[“5”,“0”,“1”,“0”,“1”],[“10”,“1”,“0”,“0”,“0”]]
解释:
点菜展示表如下所示:
Table,Beef Burrito,Ceviche,Fried Chicken,Water
3 ,0 ,2 ,1 ,0
5 ,0 ,1 ,0 ,1
10 ,1 ,0 ,0 ,0
对于餐桌 3:David 点了 “Ceviche” 和 “Fried Chicken”,而 Rous 点了 “Ceviche”
而餐桌 5:Carla 点了 “Water” 和 “Ceviche”
餐桌 10:Corina 点了 “Beef Burrito”
示例 2:

输入:orders = [[“James”,“12”,“Fried Chicken”],[“Ratesh”,“12”,“Fried Chicken”],[“Amadeus”,“12”,“Fried Chicken”],[“Adam”,“1”,“Canadian Waffles”],[“Brianna”,“1”,“Canadian Waffles”]]
输出:[[“Table”,“Canadian Waffles”,“Fried Chicken”],[“1”,“2”,“0”],[“12”,“0”,“3”]]
解释:
对于餐桌 1:Adam 和 Brianna 都点了 “Canadian Waffles”
而餐桌 12:James, Ratesh 和 Amadeus 都点了 “Fried Chicken”
示例 3:

输入:orders = [[“Laura”,“2”,“Bean Burrito”],[“Jhon”,“2”,“Beef Burrito”],[“Melissa”,“2”,“Soda”]]
输出:[[“Table”,“Bean Burrito”,“Beef Burrito”,“Soda”],[“2”,“1”,“1”,“1”]]

提示:

1 <= orders.length <= 5 * 10^4
orders[i].length == 3
1 <= customerNamei.length, foodItemi.length <= 20
customerNamei 和 foodItemi 由大小写英文字母及空格字符 ’ ’ 组成。
tableNumberi 是 1 到 500 范围内的整数。

思路

二维哈希表

代码

class Solution {
    public List<List<String>> displayTable(List<List<String>> orders) {
        TreeSet<String> dishes = new TreeSet<>();
        TreeSet<Integer> tables = new TreeSet<>();
        HashMap<Integer, HashMap<String, Integer>> map = new HashMap<>();
        for (List<String> order: orders) {
            String cust = order.get(0), tableStr = order.get(1), food = order.get(2);
            dishes.add(food);
            int table = Integer.parseInt(tableStr);
            tables.add(table);
            if (!map.containsKey(table)) {
                map.put(table, new HashMap<String, Integer>());
            }
            if (!map.get(table).containsKey(food)) {
                map.get(table).put(food, 0);
            }
            map.get(table).put(food, map.get(table).get(food) + 1);
        } 
        LinkedList<List<String>> ret = new LinkedList<>();
        LinkedList<String> head = new LinkedList<>(dishes);
        head.addFirst("Table");
        ret.add(head);
        int i = 0, j = 0;
        Iterator<Integer> iterTable = tables.iterator();
        Iterator<String> iterDish = dishes.iterator();
        while (iterTable.hasNext()) {
            LinkedList<String> row = new LinkedList<>();
            int table = iterTable.next();
            row.add(String.valueOf(table));
            iterDish = dishes.iterator();
            while (iterDish.hasNext()) {
                String food = iterDish.next();
                if (!map.get(table).containsKey(food)) {
                    row.add("0");
                } else {
                    row.add(String.valueOf(map.get(table).get(food)));
                }
            }
            ret.add(row);
        }
        return ret;
    }
}

5390. 数青蛙

给你一个字符串 croakOfFrogs,它表示不同青蛙发出的蛙鸣声(字符串 “croak” )的组合。由于同一时间可以有多只青蛙呱呱作响,所以 croakOfFrogs 中会混合多个 “croak” 。请你返回模拟字符串中所有蛙鸣所需不同青蛙的最少数目。

注意:要想发出蛙鸣 “croak”,青蛙必须 依序 输出 ‘c’, ’r’, ’o’, ’a’, ’k’ 这 5 个字母。如果没有输出全部五个字母,那么它就不会发出声音。

如果字符串 croakOfFrogs 不是由若干有效的 “croak” 字符混合而成,请返回 -1 。

示例 1:

输入:croakOfFrogs = “croakcroak”
输出:1
解释:一只青蛙 “呱呱” 两次
示例 2:

输入:croakOfFrogs = “crcoakroak”
输出:2
解释:最少需要两只青蛙,“呱呱” 声用黑体标注
第一只青蛙 “crcoakroak”
第二只青蛙 “crcoakroak”
示例 3:

输入:croakOfFrogs = “croakcrook”
输出:-1
解释:给出的字符串不是 “croak” 的有效组合。
示例 4:

输入:croakOfFrogs = “croakcroa”
输出:-1

提示:

1 <= croakOfFrogs.length <= 10^5
字符串中的字符只有 ‘c’, ‘r’, ‘o’, ‘a’ 或者 ‘k’

思路

贪心。贪心主要体现在两个地方:

  1. 遇到新字母的总是将字母分配给能够匹配的最早的croak
  2. 每次重用青蛙的时候总是用最早croak完的青蛙
    首先计算每个字符出现的次数(cnts变量)以及每个字符出现的位置(pos变量),按照贪心的原则,可以求出每个青蛙对应的c, r, o, a, k字母位置。如果『两个字符出现次数不一样』或『有一个青蛙的c, r, o, a, k字母颠倒』,则返回-1.
    再次遍历所有青蛙的croak字符组给每个croak分配青蛙同时统计青蛙个数,使用一个先进先出的队列保存每个croak字符组k字符的位置,代表已经croak完的青蛙。给每个croak分配青蛙的时候,如果队列非空则取队首的青蛙,并更新该青蛙croak完的时间重新入队;如果队列为空,则新建一只青蛙并入队。

代码

class Solution {
    private static final HashMap<Character, Integer> map = new HashMap<>() {
        {
            put('c', 0);
            put('r', 1);
            put('o', 2);
            put('a', 3);
            put('k', 4);
        }
    };
    
    public int minNumberOfFrogs(String croakOfFrogs) {
        int len = croakOfFrogs.length();
        if (len % 5 != 0) {
            return -1;
        }
        int n = len / 5, i = 0;
        int[] cnts = new int[5];
        for (char ch: croakOfFrogs.toCharArray()) {
            if (!map.containsKey(ch)) {
                return -1;
            }
            ++cnts[map.get(ch)];
        }
        for (int cnt: cnts) {
            if (cnt != n) {
                return -1;
            }
        }
        int[][] pos = new int[5][n];
        Arrays.fill(cnts, 0);
        for (i=0; i<len; ++i) {
            char ch = croakOfFrogs.charAt(i);
            if (!map.containsKey(ch)) {
                return -1;
            }
            int idx = map.get(ch);
            pos[idx][cnts[idx]++] = i;
        }
        int ans = 0;
        LinkedList<Integer> queue = new LinkedList<>();
        for (i=0; i<n; ++i) {
            if (!(pos[0][i] < pos[1][i] && pos[1][i] < pos[2][i] && pos[2][i] < pos[3][i] && pos[3][i] < pos[4][i])) {
                return -1;
            }
            queue.add(pos[4][i]);
            if (!queue.isEmpty() && queue.getFirst() < pos[0][i]) {
                queue.removeFirst();
            } else {
                ++ans;
            }
        }
        return ans;
    }
}

5391. 生成数组

给你三个整数 n、m 和 k 。下图描述的算法用于找出正整数数组中最大的元素。
在这里插入图片描述
请你生成一个具有下述属性的数组 arr :

arr 中有 n 个整数。
1 <= arr[i] <= m 其中 (0 <= i < n) 。
将上面提到的算法应用于 arr ,search_cost 的值等于 k 。
返回上述条件下生成数组 arr 的 方法数 ,由于答案可能会很大,所以 必须 对 10^9 + 7 取余。

示例 1:

输入:n = 2, m = 3, k = 1
输出:6
解释:可能的数组分别为 [1, 1], [2, 1], [2, 2], [3, 1], [3, 2] [3, 3]
示例 2:

输入:n = 5, m = 2, k = 3
输出:0
解释:没有数组可以满足上述条件
示例 3:

输入:n = 9, m = 1, k = 1
输出:1
解释:可能的数组只有 [1, 1, 1, 1, 1, 1, 1, 1, 1]
示例 4:

输入:n = 50, m = 100, k = 25
输出:34549172
解释:不要忘了对 1000000007 取余
示例 5:

输入:n = 37, m = 17, k = 7
输出:418930126

提示:

1 <= n <= 50
1 <= m <= 100
0 <= k <= n

思路

动态规划. dp[i][x][j]表示:

  • 长度为i的数组
  • search_costj
  • 数组中最大的元素为k

边界条件:

dp[1][x][1] = 1

分两种情况讨论:

  1. 当最大值x出现在数组尾端时,去掉尾端的数组search_cost减1,最大值的取值可以从1到x-1,即sum(dp[i-1][y][j-1]), y=1, 2, .., x-1
  2. 否则去掉尾端的数组search_cost不变,最大值仍为x,现数组尾端的取值小于等于x即可,对应x * dp[i-1][x][j]
    递推公式:
dp[i][x][j] = x * dp[i-1][x][j] + sum(dp[i-1][y][j-1]), y=1, 2, .., x-1

优化

直接按递推公式的时间复杂度为O(nkm^2),空间复杂度为O(nmk).
时间复杂度的优化:递推式中O(m)的sum过程可以用一个sum_dp[i][x][j]数组维护,求dp[i][x][j]的同时更新sum_dp[i][x][j],这样时间复杂度为O(nkm)
空间复杂度的优化:在ij维度上,递推公式只依赖前一个值,因此dp数组这两个维度可以取消,空间复杂度可以降到O(k)。本人太懒了,代码里面空间复杂度的优化就没有写

代码

class Solution {
    private static final int mod = 1000000007;
    
    public int numOfArrays(int n, int m, int k) {
        int[][][] dp = new int[n+1][m+1][k+1], sum_dp = new int[n+1][m+1][k+1];
        for (int x=1; x<=m; ++x) {
            dp[1][x][1] = 1;
            sum_dp[1][x][1] = x;
        }
        for (int i=1; i<=n; ++i) {
            for (int j=1; j<=k; ++j) {
                if (i == 1 && j == 1) {
                    continue;
                }
                for (int x=1; x<=m; ++x) {
                    dp[i][x][j] = sum_dp[i-1][x-1][j-1];
                    dp[i][x][j] = (int)((((long)dp[i][x][j]) + ((long) x) * ((long) dp[i-1][x][j])) % (long)mod);
                    sum_dp[i][x][j] = (sum_dp[i][x-1][j] + dp[i][x][j]) % mod;
                }
            }
        }
        int ans = 0;
        for (int x=1; x<=m; ++x) {
            ans = (ans + dp[n][x][k]) % mod;
        }
        return ans;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章