题目描述:
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一个字母只会出现在其中的一个片段。返回一个表示每个字符串片段的长度的列表。
示例 1:
输入: S = "ababcbacadefegdehijhklij"
输出: [9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
注意:
S的长度在[1, 500]之间。
S只包含小写字母'a'到'z'。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/partition-labels
注:题目要求malloc开辟数组
第一次解题思路(低效版本):
- 典型双指针思维,慢指针 k_s 指向输入数组头部,数值大小递增,快指针 k_e 指向输入数组尾部,数值大小递减;
- 对字母进行比较,如果不同,快指针-1,继续比较,直到字母相同为止,快指针停止移动,记录当前范围 [k_s_1,k_e_1];
- k_s+1,k_e回到尾部,再度执行步骤二,记录当前范围 [k_s_2,k_e_2] ,
如果 k_s_1 < k_s_2 < k_e_1, k_e_2 > k_e_1, 则更新范围到 [k_s_1,k_e_2],
如果 k_s_1 < k_s_2 < k_e_2 <= k_e_1,则无需更新范围,仍然是 [k_s_1,k_e_1]
如果 k_e_1 < k_s_2,则表示上个字母区段已查找完毕,则可以直接记录已完成字母区段长度 - 重复步骤2、3,直到 k_s 指向尾部
犯错点:
- 做遍历的时候,忘记考虑了 ababc、cabab这种c独立存在的情况,导致错误,其实只需要在对k_s、k_e数值大小做判断的时候加上等号就可以了;
- 用了一个变量去记录已分类的字母数量,在更新范围的时候,忘记去更新这个变量了,导致错误。
时间复杂度 O(n^2)
int* partitionLabels(char * S, int* returnSize)
{
int k_s = 0, k_e = 0, S_End = 0; //k_s 指向数组头部的指针 k_e 指向数组尾部的指针
int array_sum_old = 0, array_sum = 0;
int *return_value = (int *)malloc(4); //暂只开一个int空间,有需要时再加长
*returnSize = 0;
while (S[S_End] != '\0')
{
S_End++;
}
S_End--; //定位到最后一个字符
while (k_s <= S_End) //易错点:如果没有等号,会漏掉 头部或尾部 的独立字母
{ //例如 cababd 的形式 (这个=号是头部还是尾部?忘记了)
k_e = S_End; //尾指针指向末尾
while (k_s <= k_e) //易错点:如果没有等号,会漏掉 头部或尾部 的独立字母
{ //例如 cababd 的形式 (这个=号是头部还是尾部?忘记了)
if (S[k_s] != S[k_e])
k_e--;
else
{
if (k_s >= array_sum) //头指针超出了已分类部分
{
array_sum_old = array_sum; //保存已经完全分类的字符数量
array_sum = k_e + 1; //保存此次已经分类的字符数量
return_value = realloc(return_value, (*returnSize + 2) * 4);
return_value[*returnSize] = k_e - array_sum_old + 1;
*returnSize += 1;
}
else //if (k_s >= array_sum_old) //头指针在还未完全归类完成的部分
{
if (k_e >= array_sum) //尾指针在完全未分类部分
{
return_value[*returnSize - 1] = k_e + 1 - array_sum_old; //赋值
array_sum = k_e + 1; //错误点:漏写了这一句
}
else if (k_e < array_sum)
{
//在本次分类部分内部,不做任何操作
}
}
break;
}
}
k_s++; //头指针递增
}
return return_value;
}
第二次解题思路:
- 题目限定只会出现26个小写字母,将每个字母第一次、最后一次出现的下标记录下来,形成26个范围,然后对这26个范围做一次区间合并;
- 做一次顺序(或逆序)遍历,确定字母出现范围;
- 再做一次顺序遍历,下标指针k_s,取出字母S[k_s]的范围 [s1,e1],与字母S[k_s+1]的范围 [s2,e2]的范围进行比较,若有重叠部分,则更新范围 [s1,e2],若无重叠部分,则[s1,e1]是已完成分类的区间范围,计算长度即可。
错误点:
- 代码中比较范围做更新的时候,多加了一个等号,导致输出数据 从1、2、3这样的形式,变成了1、1、2、2、3、3这样的形式,原因在于多做了一次比较。
int* partitionLabels(char * S, int* returnSize)
{
*returnSize = 0; //先清零
int *return_value = (int *)malloc(4); //暂假设只有一个区段,开辟一个字节
int character_array[26][2] = { 0 };//[][0] 首次出现下标 [][1]最后一次出现下标
for (char i = 0; i < 26; i++)
character_array[i][0] = -1;
int s_index = 0;
while (S[s_index] != '\0')
{
if (character_array[S[s_index] - 'a'][0] == -1)
{
character_array[S[s_index] - 'a'][0] = s_index;
}
if (s_index > character_array[S[s_index] - 'a'][1])
{
character_array[S[s_index] - 'a'][1] = s_index;
}
s_index++;
}
s_index = 0;
int area_array[2] = { -1,-1 };
while (S[s_index] != '\0')
{
if (area_array[0] == -1)
{
area_array[0] = character_array[S[s_index] - 'a'][0]; //确定当前区间起始下标
}
if ((character_array[S[s_index] - 'a'][1] > area_array[1]))
{
area_array[1] = character_array[S[s_index] - 'a'][1]; //确定当前区间结束下标
}
s_index++;
if (s_index > area_array[1]) //一个错误点:如果加上等号,会导致多增加一个结束下标时的数组
{
*(return_value + *returnSize) = area_array[1] - area_array[0] + 1;
*returnSize += 1;
return_value = realloc(return_value, (*returnSize + 1) * 4);
area_array[0] = -1;
area_array[1] = -1;
}
}
return return_value;
}