我除了抄答案什么都不会。
题目
给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
https://leetcode-cn.com/problems/remove-duplicate-letters/
与LeetCode 1081题是一样的。
分析
感觉这题目意思很模糊。
什么是字典序:哪个字符串大取决于两个字符串中 第一个对应不相等的字符 。根据这个规则,任意一个以 a 开头的字符串都大于任意一个以 b 开头的字符串。
按这个来:
解题过程中我们将 最小的字符(a>b>c>…)尽可能的放在前面 。
方法一:贪心 栈
在遍历字符串的过程中,如果字符 i 大于字符i+1,在字符 i 不是最后一次出现的情况下,删除字符 i。
用栈来存储最终返回的字符串,并维持字符串的最小字典序。每遇到一个字符,如果这个字符不存在于栈中,就需要将该字符压入栈中。但在压入之前,需要先将之后还会出现,并且字典序比当前字符小的栈顶字符移除,然后再将当前字符压入。
代码
class Solution {
public String removeDuplicateLetters(String s) {
int[] map=new int[26];
for(int i=0;i<s.length();i++){
map[s.charAt(i)-'a']++;
}
Deque<Character> stack=new LinkedList<>();
HashSet<Character> set=new HashSet<>();
for(int i=0;i<s.length();i++){
char c=s.charAt(i);
if(!set.contains(c)){
//栈顶元素字典序小于当前元素
while(!stack.isEmpty()&&stack.peekLast()>c&&map[stack.peekLast()-'a']>0){
//之后还会出现
//移除栈顶
set.remove(stack.removeLast());
}
//当前元素入栈
set.add(c);
stack.addLast(c);
}
//当前元素可用数量-1
map[c-'a']--;
}
StringBuilder sb=new StringBuilder(stack.size());
for(Character c:stack)
sb.append(c.charValue());
return sb.toString();
}
}
复杂度
时间复杂度:O(N)。虽然外循环里面还有一个内循环,但内循环的次数受栈中剩余字符总数的限制,因此最终复杂度仍为 O(N)。
空间复杂度:O(1)。看上去空间复杂度像是O(N),但这不对!首先,set中字符不重复,其大小受字母表大小的限制。其次,只有 stack 中不存在的元素才会被压入,因此 stack 中的元素也唯一。所以最终空间复杂度为O(1)。
结果
方法二:贪心 一个字符一个字符处理
不 我不明白
每次递归中,在保证其他字符至少出现一次的情况下,确定最小左侧字符。之后再将未处理的后缀字符串继续递归。
复杂度
时间复杂度:O(N)。 每次递归调用占用O(N) 时间。递归调用的次数受常数限制(只有26个字母),最终复杂度为 O(N) * C =O(N)。
空间复杂度:O(N),每次给字符串切片都会创建一个新的字符串(字符串不可变),切片的数量受常数限制,最终复杂度为 O(N) * C = O(N)。
代码
class Solution {
public String removeDuplicateLetters(String s) {
int[] map=new int[26];
for(int i=0;i<s.length();i++){
map[s.charAt(i)-'a']++;
}
int pos=0;
for(int i=0;i<s.length();i++){
if(s.charAt(i)<s.charAt(pos))
pos=i;
if(--map[s.charAt(i)-'a']==0)
break;
}
return s.length()==0?"":s.charAt(pos)+removeDuplicateLetters(s.substring(pos+1).replaceAll(""+s.charAt(pos),""));
}
}
结果
提交击败百分五???