字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
示例:
看到这个题目,我先观察了一下示例的返回。试想一下,是根据什么来进行划分的?
如果下一个元素和当前元素不一致,就划分一个,那么结果就应该是: [“a”,“b”…]。那么肯定是不对的。
那么如果是下一个元素(i+1)以及下下个元素(i+2),都和当前元素不一致,就划分一个。貌似也不对:那么前几个应该是:[“aba”,“bcb”,“aca”…]。
到这里就能发现,从这个角度来思考肯定是走不通的,那么我们可以这么想:
首先:
那么我们可以用一个固定大小的数组,统计a-z
中的所有字母,它在字符串中最后出现的位置是哪里。
int[] lastIndex = new int[27];
for (int i = 0; i < s.length(); i++) {
lastIndex[s.charAt(i) - 'a'] = i;
}
遍历过程中,我们如何确定当前分割区间的右边界?
curMaxRight
。i
和 curMaxRight
相等,就说明到达分割边界。left
。找到分割边界的时候,左边界就好处理了,下一个左边界就是curMaxRight+1
。// 记录左边界和右边界
int left = 0, curMaxRight = 0;
for (int i = 0; i < s.length(); i++) {
// 当前这个字符对应的最原位置
int lastIndex = lastIndexArr[s.charAt(i) - 'a'];
// 遍历的同时,还要更新当前区间中重复出现的最远位置。
curMaxRight = Math.max(curMaxRight, lastIndex);
// 如果两者相等,说明当前位置就是边界值
if (curMaxRight == i) {
// 计入结果,计算当前区间的大小
res.add(curMaxRight - left + 1);
// 更新左边界
left = i + 1;
}
}
那么最终结果如下:
public List<Integer> partitionLabels(String s) {
ArrayList<Integer> res = new ArrayList<>();
int[] lastIndexArr = new int[27];
for (int i = 0; i < s.length(); i++) {
lastIndexArr[s.charAt(i) - 'a'] = i;
}
// 记录左边界和右边界
int left = 0, curMaxRight = 0;
for (int i = 0; i < s.length(); i++) {
// 当前这个字符对应的最原位置
int lastIndex = lastIndexArr[s.charAt(i) - 'a'];
// 遍历的同时,还要更新当前区间中重复出现的最远位置。
curMaxRight = Math.max(curMaxRight, lastIndex);
// 如果两者相等,说明当前位置就是边界值
if (curMaxRight == i) {
// 计入结果,计算当前区间的大小
res.add(curMaxRight - left + 1);
// 更新左边界
left = i + 1;
}
}
return res;
}