这道题看题意,很明显是我博文里面写过的有关贪心算法的两种说法之一,也就是满足每个孩子胃口的情况下,最多能喂饱多少个孩子,只不过这里的胃口变成了一个区间内部出现过的字符不能出现在其他区间,所以第一想到的就是贪心算法。
之后,我疑惑的点是如何保证一个区间里面的字符不会出现在别的区间呢?
核心思想:
策略就是不断地选择从最左边起最小的区间。可以从第一个字母开始分析,假设第一个字母是 'a',那么第一个区间一定包含最后一次出现的 'a'。但第一个出现的 'a' 和最后一个出现的 'a' 之间可能还有其他字母,这些字母会让区间变大。这样就可以不断对这个区间进行扩展,直到遍历到区间的最后一个元素,也不需要进一步扩展空间,则当前的空间确定,从i+1开始继续去确定后面的区间。
定义数组 last[char] 来表示字符 char 最后一次出现的下标。定义 anchor 和 j 来表示当前区间的首尾。如果遇到的字符最后一次出现的位置下标大于 j, 就让 j=last[c] 来拓展当前的区间。当遍历到了当前区间的末尾时(即 i==j ),把当前区间加入答案,同时将 start 设为 i+1 去找下一个区间。
class Solution { public List<Integer> partitionLabels(String S) { //首先初始化数组last,这里是寻找每一个字母对应的最后一次出现的索引数 int[] last=new int[26]; for(int i=0;i<S.length();i++){ last[S.charAt(i)-'a']=i; } int anchor=0;//记录区间首端 int j=0;//记录区间尾端 //这里由于不知道最终返回结果数组的长短,所以这里用ArrayList实现,而不是固定长度的数组 List<Integer> res=new ArrayList<>(); for(int i=0;i<S.length();i++){ j=Math.max(j,last[S.charAt(i)-'a']); if(i==j){ j=0; // res.add(i)这么写就错了,因为本体要求是返回每个区间的长度而不是索引 // anchor=i+1; // res.add(i-anchor+1);注意这么写的顺序是不对的要先对anchor进行计算,然后再更新 res.add(i-anchor+1); anchor=i+1; } } return res; } }