力扣-贪心算法-763. 划分字母区间

27 篇文章 0 订阅
26 篇文章 0 订阅

力扣-贪心算法-763. 划分字母区间

763. 划分字母区间

题目描述

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。

示例 1:
输入:S = “ababcbacadefegdehijhklij”
输出:[9,7,8]
解释:
划分结果为 “ababcbaca”, “defegde”, “hijhklij”。
每个字母最多出现在一个片段中。
像 “ababcbacadefegde”, “hijhklij” 的划分是错误的,因为划分的片段数较少。

提示:
S的长度在[1, 500]之间。
S只包含小写字母 ‘a’ 到 ‘z’ 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/non-overlapping-intervals
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:贪心算法

 这道题首先想到的是回溯算法求子串,但再仔细一想好像不需要暴力求解。又因为题目中说了字符的种类只有a到z,因此又想到哈希表。哈希表可以把字符对应的ASCII转为对应的数组下标,我们可以用数组统计出每个字符出现的总的次数。
 继续思考这道题目,题目中要求的子串应该满足子串中出现的字母在其他子串都不会出现,要尽可能多的找到这样的子串。于是我们可以把问题转化为如何判断一个子串中的字母在其他子串都没有出现过,沿着这个思路出发,我们可以记录出现的每一个字母,如果这个子串中的所有字母在后面子串出现的次数都变为0,这个子串也满足条件。但是我们要判断子串中每个字母出现的次数,就需要对子串进行二次遍历,那么时间复杂度就变为O(nn),比较高。
 既然单独判断子串中每个字符出现的次数不行,那么我们可以判断子串中所有字符的个数curTimes 与 子串中每种字符对应次数之和totalTimes是否相同。如果子串中字符每种字符对应的次数之和等于当前子串字符的个数,说明当前子串中的字母只在当前子串出现,以为如果当前子串有某个字母在后面子串还出现,那么totalTimes一定是大于curTimes的。
 所以这道题局部最优: 一旦totalTimes==curTimes,就划分子串,全局最优: 得到数量最多的子串。举不出反例,我们可以试试贪心算法那。
 判断一个字符是否在当前子串中出现时,我们用HashMap来记录,因为HashMap有直接判断一个元素是否存在的api,containsKey(key)。如果遍历到的字符没有出现过,就加入map中,并将子串对应的totalTimes加上该字符出现过的总次数,同时增加curTimes;如果遍历到的字符没有出现过,只单纯增加curTimes,其他不变。一旦curTimes=totalTimes,说明子串中的字母不会在后面出现,得到一个满足条件的子串,开始新的记录,清空HashMap。
 上述时间方法时间复杂度为O(2
n),空间复杂度为O(n)。
 答案的方式是也运用了哈希表的思想,但哈希数组里面记录的是每个字符出现的最远位置,在遍历每一个字符时,如果当前字符的位置与当前子串最远的位置相同,说明在后面的子串中不会出现当前子串中的字符,因为已经遍历到当前子串中的字符所能出现的最远位置了。答案的方法我觉得还是有迹可循的,跟力扣55. 跳跃游戏思路有点像,都是一边遍历一边更新当前最大范围。
 但我个人觉得贪心算法确实思路很妙,每次做完一题再去看答案,会,让人忍不住陶醉在思路的巧妙和代码的优雅中。于是我们得出结论 --》刷题使人快乐!
 我自己写的代码如下,代码已经加了注释,各位小伙伴如果有什么问题可以在评论里提出来,欢迎大家交流。

//贪心算法 首先统计每个字母出现的次数 用HashMap统计当前片段出现过的字母
    public List<Integer> partitionLabels(String s) {
        List<Integer> result = new LinkedList<>();//返回最终片段
        int[] sum = new int[26];//统计所有字母出现的次数
        int totalTimes = 0;//统计当前片段出现过的字母种类包含字母的个数
        int curTimes = 0;//统计当前片段总共出现了多少个字母  
        Map<Character, Integer> map = new HashMap<>();//存放当前片段出现的字母
        for (char ch : s.toCharArray()) {
            sum[ch - 'a']++;
        }
        //当前片段总共出现了字母的个数与当前片段所包含字母种类对应的所有字母个数相同时,说明后面不在有与当前片段相同的字母
        for (char ch : s.toCharArray()) {
            curTimes++;
            if (!map.containsKey(ch)) {//没有包含当前字母
                totalTimes += sum[ch - 'a'];
                map.put(ch,0);
            }
            if (curTimes == totalTimes) {
                result.add(curTimes);
                curTimes = 0;
                totalTimes = 0;
                map.clear();
            }        
        }
        return result;
        
    }

另外附上我自己搭建的个人博客网址,里面记录了我之前记录的学习心得,欢迎大家交流讨论。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
力扣是一个在线编程平台,提供了大量的算法题目,可以帮助程序员提高算法能力。回溯算法是一种搜索算法,它通过不断地尝试所有可能的解来求解问题。在回溯算法中,我们首先定义一个解空间,然后从解空间中搜索所有可能的解,直到找到符合要求的解为止。回溯算法通常用于求解组合问题、排列问题、子集问题等。 在 Java 中实现回溯算法,通常需要定义一个递归函数来搜索解空间。在递归函数中,我们首先判断当前状态是否符合要求,如果符合要求,则将当前状态加入到解集中;否则,我们继续搜索下一个状态。在搜索下一个状态时,我们需要对当前状态进行一些修改,然后递归调用自身来搜索下一个状态。当搜索完所有可能的状态后,我们需要回溯到上一个状态,继续搜索其他可能的状态。 以下是回溯算法的一般步骤: 1. 定义解空间:确定问题的解空间,并定义一个数据结构来表示解空间中的每个状态。 2. 确定约束条件:确定哪些状态是合法的,并定义一个函数来判断当前状态是否符合要求。 3. 确定搜索策略:确定搜索解空间的顺序,并定义一个函数来生成下一个状态。 4. 搜索解空间:使用递归函数搜索解空间,如果当前状态符合要求,则将其加入到解集中;否则,继续搜索下一个状态。 5. 回溯:当搜索完所有可能的状态后,回溯到上一个状态,继续搜索其他可能的状态。 以下是一个力扣题目的回溯算法 Java 实现示例: ``` class Solution { List<List<Integer>> res = new ArrayList<>(); List<Integer> path = new ArrayList<>(); public List<List<Integer>> subsets(int[] nums) { dfs(nums, 0); return res; } private void dfs(int[] nums, int start) { res.add(new ArrayList<>(path)); for (int i = start; i < nums.length; i++) { path.add(nums[i]); dfs(nums, i + 1); path.remove(path.size() - 1); } } } ``` 该算法用于求解给定数组的所有子集。在递归函数中,我们首先将当前状态加入到解集中,然后从当前位置开始搜索下一个状态。在搜索下一个状态时,我们将当前元素加入到路径中,并递归调用自身来搜索下一个状态。当搜索完所有可能的状态后,我们需要回溯到上一个状态,继续搜索其他可能的状态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值