当前位置: 首页 > 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.jsqmd.com/news/916491/

相关文章:

  • 新款奥迪 A4L 灯光升级|方款复眼透镜完美解决原车昏暗难题 - 北京新语
  • 从代码堆砌到视觉流水线:如何用 流向式卡片任务流转工具 搭建精益研发生态?
  • Sora 2世界模型的“隐形边界”被攻破:斯坦福+MIT联合团队用反事实扰动测试发现其3大泛化脆弱点(附可复现检测代码包)
  • Qt5项目直接可用的实时波形控件,含QCustomPlot封装和UI嵌入方案
  • 2026年4月钢板加工定制厂商推荐分析,不锈钢换热器管/耐酸钢管/厚壁不锈钢管/ND钢无缝钢管,钢板公司推荐 - 品牌推荐师
  • 如何用OBS RTSP服务器插件实现本地零延迟直播:新手入门完整指南
  • R3nzSkin技术解析:英雄联盟国服内存换肤实现原理
  • 2026年徐州市CPPM报名十大核心问题全流程答疑 - 众智商学院课程中心
  • 如何选择高效Markdown实时预览工具:Markn轻量级查看器的3大优势
  • 中国石油大学(北京)考研辅导班强烈推荐【独峰考研】全解析 - michalwang
  • 开发人员转AI应用开发,最大的坑是以为会调接口就够了
  • Unity游戏去马赛克终极指南:7款免费插件完整使用教程
  • 合肥包河至州电动自行车贸易:蜀山靠谱的两轮电动车租赁公司选哪家 - LYL仔仔
  • 上海海洋大学考研辅导班强烈推荐【独峰考研】全解析 - michalwang
  • 3步智能激活方案:KMS_VL_ALL_AIO一键搞定Windows与Office全系列激活
  • 2026年上海嘉定卧室家装定制公司推荐,品质服务优选 - 高定
  • AKShare金融数据接口:构建专业量化分析系统的终极指南
  • Linux各发行版介绍
  • 博客园适配器测试文章 1780117886
  • 基于TMC2208微步进驱动与DS3231 RTC的静音时钟驱动方案
  • 基于树莓派Zero 2与Coral TPU的嵌入式AI视觉设备全栈开发指南
  • 2026年超声波液位计十大品牌权威排名:国产替代加速下的选型终极指南 - 液体流量液位品牌推荐
  • 2026年盘点热门的手机录音转文字工具,高效转录适配办公学习全场景
  • 中国石油大学(华东)考研辅导班强烈推荐【独峰考研】全解析 - michalwang
  • 5个实用技巧:用Mac Mouse Fix彻底改变你的macOS鼠标体验
  • Steam创意工坊下载难题?WorkshopDL让你告别臃肿客户端
  • 【实战演练】从DVWA靶场到真实威胁:一次完整的反射型XSS攻击链复现
  • RouterOS DHCP高级玩法:巧用Option 60,实现一个接口下的多网段“智能”分配(含抓包验证步骤)
  • Arduino西蒙记忆游戏:从硬件搭建到状态机编程的嵌入式开发实战
  • 36V转5V,3.3V降压芯片,15mA以下可单LDO,以上需DC-DC预降压