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’
思路
贪心。贪心主要体现在两个地方:
- 遇到新字母的总是将字母分配给能够匹配的最早的croak
- 每次重用青蛙的时候总是用最早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_cost
为j
- 数组中最大的元素为
k
边界条件:
dp[1][x][1] = 1
分两种情况讨论:
- 当最大值
x
出现在数组尾端时,去掉尾端的数组search_cost
减1,最大值的取值可以从1到x-1
,即sum(dp[i-1][y][j-1]), y=1, 2, .., x-1
- 否则去掉尾端的数组
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)
空间复杂度的优化:在i
和j
维度上,递推公式只依赖前一个值,因此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;
}
}