LeetCode 316. 去除重复字母 java题解

我除了抄答案什么都不会。

题目

给你一个字符串 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),""));
    }
}

结果

提交击败百分五???
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值