当前位置: 首页 > news >正文

DeepSeek LeetCode 2842. 统计一个字符串的 k 子序列美丽值最大的数目 Java实现

这是一道 LeetCode 第 2842 题("统计一个字符串的 k 子序列美丽值最大的数目")的 Java 实现。

题目理解

我们需要在字符串中找出所有长度为 k 的子序列,这些子序列的美丽值最大。美丽值定义为子序列中不同字母的个数。

具体来说:

1. 找出所有长度为 k 的子序列中,不同字母的最大个数
2. 统计美丽值等于这个最大值的子序列个数
3. 结果对 10^9 + 7 取模

解题思路

1. 统计每个字母出现的频率
2. 按频率降序排序
3. 找到最大美丽值:从频率最高的字母开始选,直到凑够 k 个
4. 关键点:可能多个字母有相同频率,需要组合计算

Java 实现

```java
import java.util.*;

class Solution {
private static final int MOD = 1_000_000_007;

public int countKSubsequencesWithMaxBeauty(String s, int k) {
// 统计每个字母出现的频率
int[] freq = new int[26];
for (char c : s.toCharArray()) {
freq[c - 'a']++;
}

// 过滤掉频率为0的字母,并按频率降序排序
List<Integer> frequencies = new ArrayList<>();
for (int f : freq) {
if (f > 0) {
frequencies.add(f);
}
}
frequencies.sort((a, b) -> b - a);

// 如果字母种类少于k,无法组成k长度的子序列
if (frequencies.size() < k) {
return 0;
}

// 确定最大美丽值对应的频率分布
// 第k个频率值(0-indexed)
int threshold = frequencies.get(k - 1);

// 统计频率大于threshold的字母个数
int greaterCount = 0;
int equalCount = 0;
for (int f : frequencies) {
if (f > threshold) {
greaterCount++;
} else if (f == threshold) {
equalCount++;
}
}

// 必须从equalCount个频率为threshold的字母中选择
// 需要选择的threshold字母个数 = k - greaterCount
int needFromEqual = k - greaterCount;

// 如果needFromEqual不在有效范围内,返回0
if (needFromEqual < 0 || needFromEqual > equalCount) {
return 0;
}

// 计算组合数 C(equalCount, needFromEqual)
long combinations = comb(equalCount, needFromEqual);

// 计算每个threshold字母的贡献: threshold的needFromEqual次方
long contribution = pow(threshold, needFromEqual);

// 最终结果 = 组合数 * 贡献 mod MOD
long result = combinations % MOD * contribution % MOD;

// 乘以所有大于threshold字母的频率(它们必须被选)
// 实际上大于threshold的每个字母都有1种选择方式(必须选)
// 但我们需要乘以它们的频率乘积,因为每个字母的任意一个出现位置都可以被选
// 实际上题目要求的是子序列的个数,不是字母的组合数
// 让我重新思考...

return (int) result;
}

// 计算组合数 C(n, m) mod MOD
private long comb(int n, int m) {
if (m < 0 || m > n) return 0;
if (m == 0 || m == n) return 1;

// 使用阶乘和逆元计算
long[] fact = new long[n + 1];
long[] invFact = new long[n + 1];

fact[0] = 1;
for (int i = 1; i <= n; i++) {
fact[i] = fact[i - 1] * i % MOD;
}

invFact[n] = pow(fact[n], MOD - 2);
for (int i = n - 1; i >= 0; i--) {
invFact[i] = invFact[i + 1] * (i + 1) % MOD;
}

return fact[n] * invFact[m] % MOD * invFact[n - m] % MOD;
}

// 快速幂
private long pow(long base, long exp) {
long result = 1;
while (exp > 0) {
if ((exp & 1) == 1) {
result = result * base % MOD;
}
base = base * base % MOD;
exp >>= 1;
}
return result;
}
}
```

修正版本

上面的实现有个问题:对于频率大于 threshold 的字母,每个字母的每个出现位置都可以被选择。我们需要正确计算子序列的个数。

```java
import java.util.*;

class Solution {
private static final int MOD = 1_000_000_007;

public int countKSubsequencesWithMaxBeauty(String s, int k) {
// 统计频率
int[] freq = new int[26];
for (char c : s.toCharArray()) {
freq[c - 'a']++;
}

// 收集非零频率并排序
List<Integer> freqs = new ArrayList<>();
for (int f : freq) {
if (f > 0) {
freqs.add(f);
}
}

// 检查是否能组成k长度的子序列
if (freqs.size() < k) {
return 0;
}

// 降序排序
freqs.sort((a, b) -> b - a);

// 找到第k大的频率
int targetFreq = freqs.get(k - 1);

// 统计大于targetFreq和等于targetFreq的数量
int greater = 0;
int equal = 0;
for (int f : freqs) {
if (f > targetFreq) greater++;
else if (f == targetFreq) equal++;
}

// 需要从相等频率中选择的数量
int needFromEqual = k - greater;

// 计算组合数
long comb = nCk(equal, needFromEqual);

// 对于每个被选中的频率,该字母的所有位置都可以选择
// 但是不同的字母组合会产生不同的子序列

// 结果 = 组合数 * (targetFreq)^needFromEqual
// 再乘以所有大于targetFreq的频率(这些必须被选)
long result = comb % MOD;
result = result * pow(targetFreq, needFromEqual) % MOD;

for (int f : freqs) {
if (f > targetFreq) {
result = result * f % MOD;
}
}

return (int) result;
}

// 组合数计算 C(n, m)
private long nCk(int n, int m) {
if (m < 0 || m > n) return 0;
if (m == 0 || m == n) return 1;

long numerator = 1;
long denominator = 1;

// 使用乘法逆元
m = Math.min(m, n - m);

for (int i = 0; i < m; i++) {
numerator = numerator * (n - i) % MOD;
denominator = denominator * (i + 1) % MOD;
}

return numerator * pow(denominator, MOD - 2) % MOD;
}

private long pow(long a, long b) {
long result = 1;
a %= MOD;
while (b > 0) {
if ((b & 1) == 1) {
result = result * a % MOD;
}
a = a * a % MOD;
b >>= 1;
}
return result;
}
}
```

算法复杂度

· 时间复杂度: O(n + 26 log 26),其中 n 是字符串长度
· 空间复杂度: O(1)

核心要点

1. 美丽值最大化的策略:选择频率最高的 k 个不同字母
2. 组合计算:当多个字母频率相同时,需要从中选择部分
3. 子序列计数:每个被选中的字母,可以从它的所有出现位置中任选一个
4. 取模运算:结果对 10^9+7 取模

这个解法能够正确计算出美丽值最大的 k 子序列的个数。

http://www.gsyq.cn/news/1427802.html

相关文章:

  • Qt5项目直接可用的实时波形控件,含QCustomPlot封装和UI嵌入方案
  • 2026年徐州市CPPM报名十大核心问题全流程答疑 - 众智商学院课程中心
  • 如何选择高效Markdown实时预览工具:Markn轻量级查看器的3大优势
  • Unity游戏去马赛克终极指南:7款免费插件完整使用教程
  • 3步智能激活方案:KMS_VL_ALL_AIO一键搞定Windows与Office全系列激活
  • 2026年超声波液位计十大品牌权威排名:国产替代加速下的选型终极指南 - 液体流量液位品牌推荐
  • Linux各发行版介绍
  • 5个实用技巧:用Mac Mouse Fix彻底改变你的macOS鼠标体验
  • Arduino西蒙记忆游戏:从硬件搭建到状态机编程的嵌入式开发实战
  • RouterOS DHCP高级玩法:巧用Option 60,实现一个接口下的多网段“智能”分配(含抓包验证步骤)
  • 【实战演练】从DVWA靶场到真实威胁:一次完整的反射型XSS攻击链复现
  • 基于Microbit与PIR传感器构建运动检测报警系统
  • 京佳诚天然气销售:平谷工业气体配送公司有哪些 - LYL仔仔
  • 广东省揭阳市寄件必看!4 个全国低价上门取件平台,小件快递大件物流全拿捏,省钱又靠谱 - 时讯资讯
  • 做宣传片配乐没灵感?5个宝藏网站,轻松拿捏高级BGM!
  • Arduino Uno驱动共阳极七段数码管:从电路原理到代码实现
  • 基于Electron的跨平台图表工具构建实践:draw.io桌面版深度解析
  • Docker中编译esp32
  • 2026 年中山汽车隔音降噪第一名:南岸声学遥遥领先,军工品质铸就行业标杆 - 汽车音响改装
  • UE Niagara粒子旋转与透明度曲线设置详解:让蒲公英飘得更自然
  • 如何在Windows电脑上完美使用AirPods:终极体验增强指南
  • 2026杭州黄金回收价格解密|影响金价的核心因素+正规门店实测盘点 - 奢侈品回收测评
  • 终极网络资源下载神器:3分钟掌握全平台资源捕获技巧
  • 湖州黄金上门回收平台横评,六家主流机构实力对比 - 黄金回收
  • Lindy + Foundry + Tenderly深度集成指南(含私有测试网一键克隆脚本,前500名开发者专享)
  • 终极指南:如何用AMD Ryzen调试工具释放处理器隐藏性能
  • 广东省深圳市寄件必看!上门取件低价平台全攻略,小件快递大件物流全覆盖 - 时讯资讯
  • SPT-AKI Profile Editor:3个关键步骤彻底解决离线塔科夫存档管理难题
  • 3分钟学会:零代码在线法线贴图生成器,让2D图片变3D模型
  • 【Claude战略规划文档深度解密】:20年AI架构师亲授3大核心框架与5个致命误区