去除重复字母(LeetCode-困难)
给你一个仅包含小写字母的字符串,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证返回的结果字典序最小(要求不能打乱其他字符的相对位置)bcacb => acb
思路(递归)
例如:cfebfacb
- 准备工作,把每个字符出现的次数记录在一个数组中
- 先找到一个预处理字符的位置,从左到右遍历,没遍历一个字符,把记录数组对应的出现的次数-1,当次数== 0时,这个字符的位置就是预处理字符的位置 =》第一次找到的是e,预处理字符的位置就是e的位置
- 预处理字符 前面的字符中如果有小于它的,记录小于它的字符位置。=》c小于e的,预处理 = c的位置,获取c以后的字符串。如果没有小于e的,预处理 = e的位置
- 以预处理点 现在就变成处理点,以处理点为分割点,获取后面的子串,并把子串中所有和预处理点相等的都干掉。
- 返回:strcat(处理点字符 + 把子串交给步骤0),以子串字符=='\0‘作为递归出口
通过上面的思路走一遍例子:cfebfacb 处理点+子串
- c + febfab
- e + bacb
- a + b
- b + '\0'
结果:ceab
代码
char* delDuplicateLetters(char *string) {
//1.记录字母出现的次数
int arr[26] = {0};
for (int i = 0; i < strlen(string); i++) {
arr[string[i] - 'a'] ++;
}
//2.遍历循环字符串中的字符,每找到一个字符,对应的字母出现次数的数组中记录的出现次数--,当出现的次数减到0,说明后面不会再出现,顺便也记录一下当前遍历到的元素中,最小的元素的位置
int pos = 0;
for (int i = 0; i < strlen(string); i++) {
if (string[i] < string[pos]) {
pos = i;
}
if (--arr[string[i] - 'a'] == 0) {
break;
}
}
//3.获取有消失字符之前的最小字符的位置
char *t = (char*)malloc(sizeof(char));
*t = string[pos];
//4.根据最小字符的位置为分割点,截取后面子字符串
char subString[strlen(&string[pos + 1])];
//5.删除子串中,所有的上面循环记录中的最小元素
strcpy(subString, &string[pos + 1]);
int j = 0;
for (int i = 0; subString[i] != '\0'; i++) {
if (subString[i] != string[pos]) {
subString[j++] = subString[i];
}
}
subString[j]='\0';
//6.递归和出口
return (strlen(string) == 0) ? "" : strcat(t, delDuplicateLetters(subString));
}
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, World!\n");
char *str = delDuplicateLetters("dbcbcab");
printf("%s\n", str);
return 0;
}
运行

===我是分割线===
思路
使用栈的方式实现-参考代码中的注释
代码
代码中的栈操作,请参考文章 数据结构-栈代码实现
char* stringDelChar(char *string, char chr) {
char *ret = (char*)malloc(sizeof(char) * strlen(string));
int j = 0;
for (int i = 0; i < strlen(ret); i++) {
if (chr != ret[i]) {
ret[j++] = ret[i];
}
}
ret[j] = '\0';
return ret;
}
char* delDuplicateLetters(char *string) {
//1.字符串中的字符最后一次出现的位置
int lastPos[26] = {0};
for (int i = 0; i < strlen(string); i++) {
lastPos[string[i] - 'a'] = i;
}
//2.初始化 栈
SqStack stack;
initStack(&stack);
//3.创建一个字符串,里面存放已经入栈的字符,相当于一个容器
char *set = (char*)malloc(sizeof(char) * strlen(string));
//4.开始遍历
for (int i = 0; i < strlen(string); i++) {
char c = string[i];
//5.当前字符是否已经在容器中
char *isHas = strchr(set, c);
if (!isHas) {
SElemType topElem;
getTopElem(stack, &topElem);
//6.栈不为空 当前字符小于栈顶字符 该字符不是最后一次出现
while (!isEmptyStack(stack) && c < *topElem && lastPos[c] > i) {
//7.出栈,出容器
SElemType temp;
popElemFromStack(&stack, &temp);
set = stringDelChar(set, *temp);
}
//8.入栈,入容器
strcat(set, &c);
SElemType temp = (SElemType)malloc(sizeof(char));
*temp = '\0';
strcpy(temp, &c);
pushElem2Stack(&stack, temp);
}
}
//遍历栈,新的字符串
char *ret = (char*)malloc(sizeof(stack.top));
for (int i = 0; i <= stack.top; i++) {
SElemType c = stack.data[i];
strcat(ret, c);
}
free(set);
return ret;
}
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, World!\n");
char *str = delDuplicateLetters("cbacdcbc");
printf("%s\n", str);
return 0;
}
运行

更简洁的代码
最近学习到更加简洁的代码,整体思路和上面的栈实现一样,个人认为比我的写法更简洁的点是对c语言更透彻一些。直接上代码,代码中有我个人的注释。
char *removeDuplicateLetters(char *s) {
if (strlen(s) <= 1) {
return s;
}
//字符出现次数数组
char record[26] = {0};
//初始化栈
int len = (int)strlen(s);
char *stack = (char*)malloc(sizeof(char) * 2 * len);
memset(stack, 0, sizeof(char) * 2 * len);
int top = -1;
//循环遍历存入字符出现的次数
for (int i = 0; i < len; i++) {
record[s[i]-'a']++;
}
//循环遍历字符串中的字符
for (int i = 0; i < len; i++) {
//字符是否存在栈中
int isExist = 0;
for (int j = 0; j <= top; j++) {
if (s[i] == stack[j]) {
isExist = 1;
break;
}
}
if (isExist == 1) {
//字符存在栈中,把记录数组相关的记录--
record[s[i] - 'a']--;
} else {
//开始循环遍历,条件:栈不为空 && 栈顶元素大于当前字符 && 记录数组中当前字符的记录大于1(也就是后面还会有这个字符出现)
while (top > -1 && stack[top] > s[i] && record[stack[top]-'a'] > 1) {
//字符记录--
record[stack[top]-'a']--;
//出栈
top--;
}
//入栈当前字符
stack[++top] = s[i];
}
}
//最后给字符加入结束点
stack[++top] = '\0';
return stack;
}
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, 去除重复字符-栈!\n");
char *str = delDuplicateLetters("cbacdcbc");
printf("%s\n", str);
char *str1 = removeDuplicateLetters("cbacdcbc");
printf("%s\n", str1);
return 0;
}
