力扣-763. 划分字母区间

题目地址(763. 划分字母区间 - 力扣(LeetCode))

https://leetcode.cn/problems/partition-labels/

题目描述

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。

注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s

返回一个表示每个字符串片段的长度的列表。

 

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

示例 2:

输入:s = "eccbbbbdec"
输出:[10]

 

提示:

  • 1 <= s.length <= 500
  • s 仅由小写英文字母组成

2.题解

2.1 贪心算法

思路

这里一个组内第一个元素,我们是必须找到并且达到其最后出现位置,否则无法满足同一字母最多出现在一个片段中
在其中我们可能会遇到其他的字母,这些字母也有其最后出现位置,而且我们贪心策略必须满足:我们最后该组的停止位置必须涵盖所有遇到的字母最后出现位置
也就是说,我们贪心不断更新该组的结束位置为这些字母中最晚的一个出现位置即可

代码1

  • 语言支持:C++

C++ Code:


class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<int> letters(26), ans;
        // 初始化各字符最后出现位置
        for(int i = 0; i < s.length(); i++){
            letters[s[i] - 'a'] = i;
        }

        //设置初始值
        int endTime = -1, n = s.length();
        // 当endTime == n - 1时, 代表已经来到末尾
        while(endTime < n - 1){ // 这里 while(endTime < s.length()会不满足为 0,类型不同) 
            // 新的一组从老的最后一组向后推一个
            int cnt = 0;
            int startTime = endTime + 1; // 向后推一个
            endTime = letters[s[endTime + 1] - 'a']; // 找到新组第一个字母最后出现的位置
            // 一个新的组别
            while(startTime <= endTime){
                cnt++; // 记录次数
                endTime = max(endTime, letters[s[startTime]- 'a'] ); // 更新一组内最后出现的字母位置
                startTime++; // 向后推进
            }
            ans.push_back(cnt);
        }
        return ans;
    }
};

代码2-改进

我代码一种的逻辑有一些复杂,两个while循环嵌套,初始值的设置-1,endTime的两次更新,这些层层相嵌,导致逻辑很容易混乱
所以根据力扣题解代码进行了改进:
这里其实只需要一个for循环即可,然后实时更新最新的endTime, 计算cnt也不需要每次cnt++, 只需要知道起始位置startTime和endTime即可
再下一组的时候,只需要startTime = endTime + 1;就相当于分组了,不需要单独对于二者都进行重新设置了, i++作为遍历迭代器而不是startTime了!!!

class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<int> letters(26), ans;
        // 初始化各字符最后出现位置
        for(int i = 0; i < s.length(); i++){
            letters[s[i] - 'a'] = i;
        }

        int startTime = 0, endTime = 0;
        for(int i = 0; i < s.length(); i++){
            endTime = max(endTime, letters[s[i] - 'a']);
            if(i == endTime){
                int cnt = endTime - startTime + 1;
                ans.push_back(cnt);
                startTime = endTime + 1;;
            }
        }
        return ans;
    }
};

复杂度分析

·时间复杂度:\(O(n)\),其中\(n\)是字符串的长度。需要遍历字符串两次,第一次遍历时记录每个字母最后一次出现的下标位置,第二次遍历时进行字符串的划分。

空间复杂度\(:O(|\Sigma|)\),其中 Σ是字符串中的字符集。这道题中,
字符串只包含小写字母,因此\(|\Sigma|=26\)

posted @ 2024-06-20 16:43  DawnTraveler  阅读(12)  评论(0编辑  收藏  举报