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

贪心——划分字母区间

题目要求:

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。例如,字符串 "ababcc" 能够被分为 ["abab", "cc"],但类似 ["aba", "bcc"] 或 ["ab", "ab", "cc"] 的划分是非法的。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。
返回一个表示每个字符串片段的长度的列表。

解题思路

要解决这个问题,核心思路是贪心算法

  1. 先记录字符串中每个字符最后一次出现的下标,这是关键 —— 因为同一个字符必须只在一个片段里,所以片段的结束位置至少要到这个字符最后出现的位置。

  2. 遍历字符串,动态维护当前片段的最远结束下标

  • 每遍历一个字符,更新当前片段的最远结束下标为「当前最远下标」和「该字符最后下标」的最大值。
  • 当遍历到当前片段的最远结束下标时,说明这个片段可以分割,记录长度,然后开始新的片段。

代码解释:

  1. 记录字符最后出现位置:
    用长度为 26 的数组lastIndex存储每个小写字母最后一次出现的下标,空间复杂度 O (1)(固定大小数组)。

  2. 贪心遍历分割:

  • start:当前片段的起始下标。
  • end:当前片段能到达的最远下标(由片段内所有字符的最后出现位置决定)。
  • 当遍历到i == end时,说明当前片段内所有字符都不会出现在后续位置,完成分割,记录长度。

复杂度

  • 时间复杂度:O (n),仅遍历字符串两次(一次记录最后位置,一次分割)。
  • 空间复杂度:O (1),除结果列表外,仅使用固定大小的辅助数组。

总结

  1. 核心是先记录每个字符最后出现的位置,再用贪心策略动态确定片段边界。
  2. 遍历到当前片段最远结束下标时分割,保证同一字母只在一个片段,且分割片段最多。
  3. 时间复杂度 O (n),效率最优,适合处理长字符串。

Java代码实现如下

import java.util.ArrayList;
import java.util.List;public class PartitionLabels {public List<Integer> partitionLabels(String s) {// 1. 记录每个字符最后一次出现的索引int[] lastIndex = new int[26]; // 小写字母,用26长度数组即可int n = s.length();for (int i = 0; i < n; i++) {char ch = s.charAt(i);lastIndex[ch - 'a'] = i; // 字符转数组下标:ch-'a'}List<Integer> result = new ArrayList<>();int start = 0; // 片段起始下标int end = 0;   // 片段最远结束下标// 2. 遍历字符串,贪心分割for (int i = 0; i < n; i++) {char ch = s.charAt(i);// 更新当前片段的最远结束位置end = Math.max(end, lastIndex[ch - 'a']);// 遍历到当前片段的最远位置,分割成功if (i == end) {result.add(end - start + 1); // 记录片段长度start = i + 1; // 更新下一个片段的起始位置}}return result;}// 测试示例public static void main(String[] args) {PartitionLabels solution = new PartitionLabels();// 测试用例1:"ababcc" → 输出 [4,2]System.out.println(solution.partitionLabels("ababcc"));// 测试用例2:"ababcbacabcdefegdehijhklij" → 输出 [9,7,8]System.out.println(solution.partitionLabels("ababcbacabcdefegdehijhklij"));}
}

解题思路

  1. 由于同一个字母最多出现在一个片段中,因此我们要确保这个片段能覆盖到当前字符的最远位置,我们其实不需要特别地去找字符最后一次出现的索引,我们只需要在遍历的过程中,每次都在循环体中记录当前字符的位置,那么当遍历到同一个字符最后一次出现的位置时,就会自然地更新为字符最后一次出现的位置

  2. 由于英文小写字母总共有26个,因此我们只需要创建一个长度为26的整型数组,通过关系式ch - 'a'可以得到当前字符与英文字母a的差值,这个差值代表了字符对应的数组索引,此时英文字母就可以用数组的下标唯一表示,例如ch[0]对应字母a。

// 1. 记录每个字符最后一次出现的索引int[] lastIndex = new int[26]; // 小写字母,用26长度数组即可int n = s.length(); //字符串的长度是调用函数length(),数组的长度不是函数因此不需要加括号,直接arr.lengthfor (int i = 0; i < n; i++) {char ch = s.charAt(i);lastIndex[ch - 'a'] = i; // 字符转数组下标:ch-'a'}
  1. 由于需要返回一个表示每个字符串片段的长度的列表。我们创建了一个集合用来记录每个片段的长度,为了帮助我们更好地分割字符串并且记录片段的长度,我们创建了一对双指针start和end,分别对应片段的起始索引结束索引
 List<Integer> result = new ArrayList<>();int start = 0; // 片段起始下标int end = 0;   // 片段最远结束下标
  1. 第一个for循环是为了记录每个字符最后一次出现的索引,第二个for循环目的是为了分割字符串
    那么如何有了上述工具,我们该如何贪心分割呢?其实就是将当前遍历到的字符最后一次出现的索引与当前片段的最远位置索引进行比较更新为最大的那个确保当前片段覆盖了片段中每个字符最后出现的位置。
for(int i = 0;i<n;i++){
char ch = s.charAt(i);
end = Math.max(end,lastIndex[ch-'a')];
  1. 当循环遍历到当前片段的最远位置后(i==end),我们就可以对该片段进行分割了,结合前面引入的双指针,用公式end-start+1,就得到了当前片段的长度,将这个长度加入到结果集合result中,同时更新start的位置为i+1实现片段的分割
if(i==end){result.add(end - start + 1);start = i+1;}
}

完成第一个片段的分割后,start来到了第二个片段的起始索引位置(i+1),循环继续往后遍历字符串,不断更新下一个片段的最远位置(end = Math.max(end,lastIndex(ch-'a'))),直到到达片段的最远位置(i==end),用(end - start + 1)记录当前片段的长度,将长度加到结果集合中,result.add(end - start + 1),再更新start为下一个片段的起始索引(start = i + 1)

这就是字符串片段划分的贪心策略解法

http://www.jsqmd.com/news/823855/

相关文章:

  • COLMAP重建翻车了?NeRF数据预处理中相机位姿估计的3个常见陷阱与调试技巧
  • AI专著生成工具评测:快速产出20万字专著,哪款最值得用?
  • 从Web空间到邮件服务器:Linux磁盘配额quota的3个真实生产环境应用案例详解
  • Source Han Serif CN:7款免费开源字体如何重塑你的中文排版体验
  • C语言条件编译:从语法到工程实践的高级应用指南
  • 它正在定义云安全的AI时代?深度拆解快快云安全AI大模型凭啥突围
  • 2026年智能电话外呼机器人厂家优质推荐榜亲测结果
  • 使用Taotoken的API Key管理功能实现安全的访问控制与审计
  • 告别Activity地狱!用XPage框架3.0.0重构你的Android应用,一个容器搞定所有页面
  • 3大协议支持:LuckyLilliaBot如何让QQ机器人开发更高效
  • 豆包大模型流式响应实战
  • 同城双活:交易链路的稳定性与可靠性探索
  • 使用Taotoken后API调用延迟与稳定性的一月观测记录
  • AI原生IDE新范式:深度解析TRAE的三种协作模式的集成实践
  • 5分钟搞定B站视频下载:BilibiliDown完整指南
  • IP定位系统源码二开版 新增分销功能 PHP地理位置查询系统
  • Kirara AI:模块化框架助力开发者快速构建AI应用与智能体
  • Termius中文版:零门槛掌握专业远程管理的终极指南
  • Obsidian加密插件终极指南:如何安全保护你的私密笔记
  • 终极免费FF14钓鱼计时器:渔人的直感完整使用指南
  • 人生第一双高跟鞋品牌排行 轻奢品质与适配性实测 - 奔跑123
  • 番茄小说下载器:永久保存你喜爱的电子书,打造个人数字图书馆 [特殊字符]
  • 3大核心能力解析:Vin象棋如何用深度学习重塑中国象棋AI辅助体验
  • 基于PaddleOCR的银行卡识别:从预处理到后处理的工程化实践
  • 为内部工具编写 Python 脚本调用 Taotoken 各类模型的最小示例
  • 2026 云手机横评:傲晨云、多多云、六边云、桃心云实测,全能旗舰实至名归
  • 大厂技术面试官告诉你:我们到底在招什么样的人?
  • Linux文件传输:SCP与Rsync原理、实战与自动化指南
  • 告别盲人摸象:用Wireshark抓包分析树莓派MIPI CSI/DSI数据流(实战篇)
  • 对比自行维护API密钥,使用Taotoken Token Plan套餐的成本观察