去除重复字母
LeetCode 级别:困难
Q: 题目
给你一个仅包含小写字母的字符串,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证返回结果的字典序最小。要求:不能打乱其他字符的相对位置。原题目leetcode链接
示例1:
输入:"bcabc"
输出:"abc"
示例2:
输入:"cbacdcbc"
输出:"acdb"
~~cbad~~
, ~~bacd~~
, ~~adcb~~
Analyze:
分析关键字:字典序、不打乱
- 字典序最小:即排列顺序按照26个字母一次排序,比如给的
antman
肯定要排在ultraman
前面 - 不打乱排序:即去除重复字母后,未被重复的那部分字母,不改变原来位置。比如
bcabc
结果是abc
那么去除的是第1、第2位的b
和c
, 后面3位abc
依然不变。
A:思路:
给定字符串
s
,排除字符串异常情况;使用一个
record
数组,来记录字符串中字母出现的次数;创建一个栈
stack
,用来存储去除重复字母的结果,并利用它的特性帮助我们找到正确的次序。遍历给定的字符串
s
;从0~
top
依次遍历stack
,判断当前的字符s[i]
是否存在于stack
中,用一个int
类型的值isExist
来表示是否存在。如果
isExsit
存在,即==1,将record [s[i]]
位置上出现次数减1,即record[s[i]]--
,并继续遍历下一个字符;意思是,当前的stack 已经包含这个字符,以后都不需要继续处理这个字符了。如果
isExist
不存在,则需要while
循环一下,找到这个字符正确的位置,然后存储起来。循环规则大概是这样:跳过栈中比当前字符大、而且后面还会出现的元素,将字符入栈。while
循环的条件如下:- 用
top
> -1 来保证栈不为空 - 用
stack[top]
> s[i] 表示栈定元素比当前元素大 - 用
record[stack[top]]
> 1 表示后面还会出现
- 用
等遍历完所有的字符后,对当前的字符栈
stack
添加一个结束符\0
,并返回当前字符串首地址;代码实现
char *removeDuplicateLetters(char *s)
{
/*
① 特殊情况处理,s为空,或者字符串长度为0;
② 特殊情况,s的长度为1,则没有必要后续的处理,则直接返回s;
*/
if (s == NULL || strlen(s) == 0) {
return "";
}
if (strlen(s) == 1) {
return s;
}
//record数组,用来记录字符串s中每个字符未来会出现的次数;
char record[26] = {0};
int len = (int)strlen(s);
//申请一个字符串stack;(用栈的特性来进行stack字符串的数据进出)
char* stack = (char*)malloc(len * 2 * sizeof(char));
//memset(void *s, int ch, size_t n) 将stack len*2*sizeof(char)长度范围的空间填充0;
memset(stack, 0, len * 2 * sizeof(char));
//stack 栈顶赋初值为-1;
int top = -1;
//1.统计每个字符的频次
//例如bcabc recod[26] = {1,2,2};
int i;
for (i = 0; i < len; i++) {
record[s[i] - 'a']++;
}
//2.遍历s,入栈
for (i = 0; i < len; i++) {
//isExist 标记, 判断当前字符是否存在栈中;
int isExist = 0;
//①从0~top,遍历stack 判断当前字符s[i]是否存在于栈stack中
//如果当前字符是否存在于栈的flag, 0表示不存在, 1表示存在
//top指向栈顶(也是执行stack字符串最后一个字符的位置,表示字符串长度上限)
for (int j = 0; j <= top; j++) {
if (s[i] == stack[j]) {
isExist = 1;
break;
}
}
//② 如果存在,record[s[i]]位置上的出现次数减一,并继续遍历下一个字符
//③ 如果不存在,则需要循环一个正确位置存储起来;
//④ 如果不存在,跳过栈中所有比当前字符大、且后面还会出现的元素,然后将当前字符入栈
// top > -1表示栈非空
//stack[top] > s[i]表示栈顶元素比当前元素大
//record[stack[top]] > 1表示后面还会出现
//例如b,c因为不符合以下条件会直接入栈.stack[] = "bc",但是当当前字符是"a"时,由于bcabc,a不应该是在stack的顺序是"bca",所以要把位置不符合的字符出栈;
//top = 1,stack[top] > s[i], c>a; 并且stack[top] 在之后还会重复的出现,所以我们可以安心的把stack中的栈顶C出栈,所以stack[]="b",top减一后等于0; 同时也需要将record[c]出现次数减一;
//top=0,stack[top]>s[i],b>a,并且stack[top] 在之后还会出现,所以stack把栈顶b出栈,所以此时栈stack[]="",top减一后等于-1, 此时栈中位置不正确的字符都已经移除;
if (isExist == 1) {
record[s[i] - 'a']--;
} else {
while (top > -1 && stack[top] > s[i] && record[stack[top] - 'a'] > 1) {
// 跳过该元素,频次要减一
record[stack[top] - 'a']--;
// 出栈
top--;
}
//⑤ 结束while 循环;
//循环结束的3种可能性:(1)移动到栈底(top == -1) ; (2)栈顶元素小于当前元素(stack[top] <= s[i]) (3)栈顶元素后面不出现(record[stack[top]] == 1)
// 此时,当前元素要插入到top的下一个位置
// top往上移动1位
top++;
// 入栈
stack[top] = s[i];
}
}
//结束栈顶添加字符结束符
stack[++top] = '\0';
return stack;
}
试着执行一下代码:
char *s ;
s = removeDuplicateLetters("zdzdaabc");
结果如下:
从 zdzdaabc
= > dzabc
,符合要求 ✅
提交结果
leetcode 双百,可以可以