力扣 763. 划分字母区间

发布时间 2023-04-25 16:31:30作者: 付玬熙

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

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

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

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

示例 2:

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

提示:

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

题解

因为要求"每个字母最多出现在一个片段中",所以对于同一个字母,所在片段应该包括它第一次出现到最后一次出现,所以需要记录每个字母最后一次出现的位置,

使用长度为26int数组记录每个字母最后一次出现的位置,遍历s,通过s[i]-'a'得到在记录数组last_idx中的下标。如s = "ababcbacadefegdehijhklij",得到a对应的last_idx[0]=8,即a最后一次出现在下标8b对应的last_idx[1]=5,b最后一次在下标5出现。

接下来划分片段,寻找每个片段可能的最小结束下标end,同时需要变量记录开始下标start。遍历s,对于当前字母cur,查询last_idx得到最后一次出现的下标endCur

  • 更新end=max(end,endCur),更新当前区间的结束下标为原下标和endCur较大的一个,因为当前元素最后一次在endCur出现,那么当前区间的分割点>=endCur,否则无法满足"每个字母最多出现在一个片段中"。

那么end是如何保证为当前片段的结束下标呢?如先遍历a,则end=enda=8,即为了满足把a都切在一个片段中,需要至少从8开始切,接下来遍历到bend=enda>endb=5,从8开始切仍然可以满足把b都切在一个片段中,如果在遍历到enda之前,出现字母curendCur>enda,则end更新为endCur,表示即便从8开始切割可以满足a和b,但是无法满足字母cur,所以当前区间需要在endCur结束。

  • 如果i==end,到达当前区间的结束下标,切割得到当前区间长度为end-start+1,同时更新下一个区间的起始位置start为当前区间结束下标end+1

如果还是不能理解可以看:https://www.youtube.com/watch?v=5NCjHqx2v-k

查看代码
 class Solution {
public:
    vector<int> partitionLabels(string s) {
        int last_idx[26]={0};
        //遍历s,更新26个字母最后一次出现的位置
        for(int i=0;i<s.length();++i){
            last_idx[s[i]-'a']=i;
        }
        //记录结果
        vector<int> res;
        int start=0;//区间开始下标
        int end=0;//区间结束下标
        for(int i=0;i<s.length();++i){
            end=end>last_idx[s[i]-'a']?end:last_idx[s[i]-'a'];//更新区间的结尾为较大值
            //当达到区间结尾了
            if(i==end){
                res.emplace_back(end-start+1);//记录区间长度
                start=end+1;//更新区间开始下标
            }
        }
        return res;
    }
};