Skip to content

Commit 07db44c

Browse files
author
robot
committed
feat: $2842, $2866
1 parent cb52959 commit 07db44c

File tree

2 files changed

+234
-0
lines changed

2 files changed

+234
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
## 题目地址(2842. 统计一个字符串的 k 子序列美丽值最大的数目)
2+
3+
https://leetcode.cn/problems/count-k-subsequences-of-a-string-with-maximum-beauty/
4+
5+
## 题目描述
6+
7+
```
8+
给你一个字符串 s 和一个整数 k 。
9+
10+
k 子序列指的是 s 的一个长度为 k 的 子序列 ,且所有字符都是 唯一 的,也就是说每个字符在子序列里只出现过一次。
11+
12+
定义 f(c) 为字符 c 在 s 中出现的次数。
13+
14+
k 子序列的 美丽值 定义为这个子序列中每一个字符 c 的 f(c) 之 和 。
15+
16+
比方说,s = "abbbdd" 和 k = 2 ,我们有:
17+
18+
f('a') = 1, f('b') = 3, f('d') = 2
19+
s 的部分 k 子序列为:
20+
"abbbdd" -> "ab" ,美丽值为 f('a') + f('b') = 4
21+
"abbbdd" -> "ad" ,美丽值为 f('a') + f('d') = 3
22+
"abbbdd" -> "bd" ,美丽值为 f('b') + f('d') = 5
23+
请你返回一个整数,表示所有 k 子序列 里面 美丽值 是 最大值 的子序列数目。由于答案可能很大,将结果对 109 + 7 取余后返回。
24+
25+
一个字符串的子序列指的是从原字符串里面删除一些字符(也可能一个字符也不删除),不改变剩下字符顺序连接得到的新字符串。
26+
27+
注意:
28+
29+
f(c) 指的是字符 c 在字符串 s 的出现次数,不是在 k 子序列里的出现次数。
30+
两个 k 子序列如果有任何一个字符在原字符串中的下标不同,则它们是两个不同的子序列。所以两个不同的 k 子序列可能产生相同的字符串。
31+
32+
33+
示例 1:
34+
35+
输入:s = "bcca", k = 2
36+
输出:4
37+
解释:s 中我们有 f('a') = 1 ,f('b') = 1 和 f('c') = 2 。
38+
s 的 k 子序列为:
39+
bcca ,美丽值为 f('b') + f('c') = 3
40+
bcca ,美丽值为 f('b') + f('c') = 3
41+
bcca ,美丽值为 f('b') + f('a') = 2
42+
bcca ,美丽值为 f('c') + f('a') = 3
43+
bcca ,美丽值为 f('c') + f('a') = 3
44+
总共有 4 个 k 子序列美丽值为最大值 3 。
45+
所以答案为 4 。
46+
示例 2:
47+
48+
输入:s = "abbcd", k = 4
49+
输出:2
50+
解释:s 中我们有 f('a') = 1 ,f('b') = 2 ,f('c') = 1 和 f('d') = 1 。
51+
s 的 k 子序列为:
52+
abbcd ,美丽值为 f('a') + f('b') + f('c') + f('d') = 5
53+
abbcd ,美丽值为 f('a') + f('b') + f('c') + f('d') = 5
54+
总共有 2 个 k 子序列美丽值为最大值 5 。
55+
所以答案为 2 。
56+
57+
58+
提示:
59+
60+
1 <= s.length <= 2 * 105
61+
1 <= k <= s.length
62+
s 只包含小写英文字母。
63+
```
64+
65+
## 前置知识
66+
67+
- 排列组合
68+
69+
## 思路
70+
71+
显然我们应该贪心地使用频率高的,也就是 f(c) 大的 c。
72+
73+
因此一个思路就是从大到小选择 c,由于同一个 c 是不同的方案。因此选择 c 就有 f(c) 种选法。
74+
75+
如果有两个相同频率的,那么方案数就是 f(c) * f(c)。 如果有 k 个频率相同的,方案数就是 f(c) ** k。
76+
77+
如果有 num 个频率相同的要选,但是只能选 k 个,k < num。那么就可以从 num 个先选 k 个,方案数是 C_{num}^{k},然后再用上面的计算方法计算。
78+
79+
最后利用乘法原理,将依次选择的方案数乘起来就好了。
80+
81+
## 代码
82+
83+
- 语言支持:Python3
84+
85+
Python3 Code:
86+
87+
```python
88+
class Solution:
89+
def countKSubsequencesWithMaxBeauty(self, s: str, k: int) -> int:
90+
MOD = 10 ** 9 + 7
91+
ans = 1
92+
cnt = Counter(Counter(s).values())
93+
for c, num in sorted(cnt.items(), reverse=True):
94+
# c 是出现次数
95+
# num 是出现次数为 c 的有多少个
96+
if num >= k:
97+
return ans * pow(c, k, MOD) * comb(num, k) % MOD
98+
ans *= pow(c, num, MOD) * comb(num, num) % MOD
99+
k -= num
100+
return 0
101+
102+
```
103+
104+
105+
**复杂度分析**
106+
107+
令 n 为数组长度
108+
109+
- 时间复杂度:$O(nlogn)$
110+
- 空间复杂度:$O(n)$
111+
112+
主要的时间在于排序。
113+

problems/2866.beautiful-towers-ii.md

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
## 题目地址(2866. 美丽塔 II)
2+
3+
https://leetcode.cn/problems/beautiful-towers-ii/description/
4+
5+
## 题目描述
6+
7+
```
8+
给你一个长度为 n 下标从 0 开始的整数数组 maxHeights 。
9+
10+
你的任务是在坐标轴上建 n 座塔。第 i 座塔的下标为 i ,高度为 heights[i] 。
11+
12+
如果以下条件满足,我们称这些塔是 美丽 的:
13+
14+
1 <= heights[i] <= maxHeights[i]
15+
heights 是一个 山状 数组。
16+
如果存在下标 i 满足以下条件,那么我们称数组 heights 是一个 山状 数组:
17+
18+
对于所有 0 < j <= i ,都有 heights[j - 1] <= heights[j]
19+
对于所有 i <= k < n - 1 ,都有 heights[k + 1] <= heights[k]
20+
请你返回满足 美丽塔 要求的方案中,高度和的最大值 。
21+
22+
23+
24+
示例 1:
25+
26+
输入:maxHeights = [5,3,4,1,1]
27+
输出:13
28+
解释:和最大的美丽塔方案为 heights = [5,3,3,1,1] ,这是一个美丽塔方案,因为:
29+
- 1 <= heights[i] <= maxHeights[i]
30+
- heights 是个山状数组,峰值在 i = 0 处。
31+
13 是所有美丽塔方案中的最大高度和。
32+
示例 2:
33+
34+
输入:maxHeights = [6,5,3,9,2,7]
35+
输出:22
36+
解释: 和最大的美丽塔方案为 heights = [3,3,3,9,2,2] ,这是一个美丽塔方案,因为:
37+
- 1 <= heights[i] <= maxHeights[i]
38+
- heights 是个山状数组,峰值在 i = 3 处。
39+
22 是所有美丽塔方案中的最大高度和。
40+
示例 3:
41+
42+
输入:maxHeights = [3,2,5,5,2,3]
43+
输出:18
44+
解释:和最大的美丽塔方案为 heights = [2,2,5,5,2,2] ,这是一个美丽塔方案,因为:
45+
- 1 <= heights[i] <= maxHeights[i]
46+
- heights 是个山状数组,最大值在 i = 2 处。
47+
注意,在这个方案中,i = 3 也是一个峰值。
48+
18 是所有美丽塔方案中的最大高度和。
49+
50+
51+
提示:
52+
53+
1 <= n == maxHeights <= 105
54+
1 <= maxHeights[i] <= 109
55+
```
56+
57+
## 前置知识
58+
59+
- 动态规划
60+
- 单调栈
61+
62+
## 思路
63+
64+
这是一个为数不多的 2000 多分的中等题,难度在中等中偏大。
65+
66+
枚举 i 作为顶峰,其取值贪心的取 maxHeight[i]。关键是左右两侧如何取。由于左右两侧逻辑没有本质区别, 不妨仅考虑左边,然后套用同样的方法处理右边。
67+
68+
定义 f[i] 表示 i 为峰顶,左侧高度和最大值。我们可以递推地计算出所有 f[i] 的值。同理 g[i] 表示 i 为峰顶,右侧高度和最大值。
69+
70+
当 f 和 g 都已经处理好了,那么枚举 f[i] + g[i] - maxHeight[i] 的最大值即可。之所以减去 maxHeight[i] 是因为 f[i] 和 g[i] 都加上了当前位置的高度 maxHeight[i],重复了。
71+
72+
那么现在剩下如何计算 f 数组,也就是递推公式是什么。
73+
74+
我们用一个单调栈维护处理过的位置,对于当前位置 i,假设其左侧第一个小于它的位置是 l,那么 [l + 1, i] 都是大于等于 maxHeight[i] 的, 都可以且最多取到 maxHeight[i]。可以得到递推公式 f[i] = f[l] + (i - l) * maxHeight[i]
75+
76+
77+
## 代码
78+
79+
- 语言支持:Python3
80+
81+
Python3 Code:
82+
83+
```python
84+
class Solution:
85+
def maximumSumOfHeights(self, maxHeight: List[int]) -> int:
86+
# 枚举 i 作为顶峰,其取值贪心的取 maxHeight[i]
87+
# 其左侧第一个小于它的位置 l,[l + 1, i] 都可以且最多取到 maxHeight[i]
88+
n = len(maxHeight)
89+
f = [-1] * n # f[i] 表示 i 为峰顶,左侧高度和最大值
90+
g = [-1] * n # g[i] 表示 i 为峰顶,右侧高度和最大值
91+
def cal(f):
92+
st = []
93+
for i in range(len(maxHeight)):
94+
while st and maxHeight[i] < maxHeight[st[-1]]:
95+
st.pop()
96+
# 其左侧第一个小于它的位置 l,[l + 1, i] 都可以且最多取到 maxHeight[i]
97+
if st:
98+
f[i] = (i - st[-1]) * maxHeight[i] + f[st[-1]]
99+
else:
100+
f[i] = maxHeight[i] * (i + 1)
101+
st.append(i)
102+
cal(f)
103+
maxHeight = maxHeight[::-1]
104+
cal(g)
105+
maxHeight = maxHeight[::-1]
106+
ans = 0
107+
for i in range(len(maxHeight)):
108+
ans = max(ans, f[i] + g[n - 1 - i] - maxHeight[i])
109+
return ans
110+
```
111+
112+
113+
**复杂度分析**
114+
115+
令 n 为数组长度
116+
117+
- 时间复杂度:$O(n)$
118+
- 空间复杂度:$O(n)$
119+
120+
f 和 g 以及 st 都使用 n 的空间。并且我们仅遍历了 maxHeights 数组三次,因此时间和空间复杂度都是 n。
121+

0 commit comments

Comments
 (0)