763.划分字母区间
763.划分字母区间
题目
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
示例:
输入:S = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
提示:
S的长度在[1, 500]之间。
S只包含小写字母 'a' 到 'z'
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/partition-labels
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
划分尽可能多的片段,那么每段的字母尽可能少。
每段包含的字母要求是不出现在其他段,那记录下该字母出现的最后位置,如acxxxxxa,这一串字符串肯定是在一段。acxxxxac循环到c的时候,发现c的最后位置比a的最后位置大,那么更新c出现的最后位置为右边界,如果循环到了右边界,说明这段字符所含的字母不出现在其他段了。
这样在循环的过程中,每个字母都要去找最后出现的位置,包括了已经找过字母,如axxaxxa,这样是很浪费时间的。我们希望出现过的字母不再去找它最后出现的位置,由于字母的范围限定在了所有的小写字母,所以可以用一个大小26的boolean数组来记录该字母是否出现过。

代码
一般划分区间问题都需要用到两个指针,left与right来标记区间的起始位置与结束位置。
class Solution {
public List<Integer> partitionLabels(String s) {
int left=0, right=0;
List <Integer> res = new ArrayList <>();
boolean [] flag = new boolean[26];//记录字母是否出现过
for(int i =0;i<s.length();i++){
char cur = s.charAt(i);
//如果没出现过,寻找最后一次出现的位置为右边界,如果出现过,不用管这个判断
if(!flag[cur-'a']){
//更新右边界
right = Math.max(right,s.lastIndexOf(cur));
flag[cur-'a'] = true;
}
//判断当前i是否到达了右边界。
if(right==i){
//计算这个片段的长度
res.add(right-left+1);
//下一个片段起始位置
left = i + 1;
}
}
return res;
}
}
这里直接使用了lastIndexof方法,下面我们采用不使用封装好的lastIndexof方法。
先循环一次,更新每个字母出现的最后的位置。
class Solution {
public List<Integer> partitionLabels(String s) {
List<Integer> res = new ArrayList<>();
int[] maxIndex = new int[26];
int len = s.length();
//遍历记录字符串中每个字母最大下标位置
//相当于自己写的lastIndexof
for(int i=0;i<len;i++){
maxIndex[s.charAt(i)-'a'] = i;
}
int end=0;
int start=0;
for(int j=0;j<len;j++){
end = Math.max(end,maxIndex[s.charAt(j)-'a']);
if(j==end){
res.add(end-start+1);
start = end+1;
}
}
return res;
}
}
浙公网安备 33010602011771号