Leetcode-316-去除重复字母

题目说明

给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。

示例 1:
输入:s = "bcabc"
输出:"abc"

示例 2:
输入:s = "cbacdcbc"
输出:"acdb"

提示:
1 <= s.length <= 104
s 由小写英文字母组成

题目分析

首先要知道什么叫 “字典序”。
字符串之间比较跟数字之间比较是不太一样的:字符串比较,是从头往后一个字符一个字符比较的,哪个字符串大取决于两个字符串中第一个对应不相等的字符。
所以,任意一个以 a 开头的字符串都大于任意一个以 b 开头的字符串。
为了得到最小字典序的结果,解题过程中,我们可以将最小的字符尽可能的放在前面,把前面出现的重复字母全部删除。这其实就是一个贪心策略。

方法一:贪心策略(逐个字符处理)

这种想法就是典型的贪心策略了:我们每次都找到当前能够移到最左边的、最小的字母。这就是我们得到结果的第一个字母,它之前的所有重复字母会被删掉;然后我们从它以后的字符串中,使用相同的逻辑,继续寻找第二个最小的字母。
所以,我们在代码实现上,可以使用递归。

代码实现:

public class RemoveDuplicateLetters {
    public String removeDuplicateLetters(String s) {
        if (s.length() == 0) 
        	return "";
        int position = 0;
        for (int i = 0; i < s.length(); i++){
            if (s.charAt(i) < s.charAt(position)){
                boolean isReplaceable = true;
                for (int j = position; j < i; j++){
                    boolean isDuplicated = false;
                    for (int k = i; k < s.length(); k++){
                        if (s.charAt(k) == s.charAt(j)){
                            isDuplicated = true;
                            break;
                        }
                    }
                    isReplaceable = isReplaceable && isDuplicated;
                }

                if (isReplaceable){
                    position = i;
                }
            }
        }
        return s.charAt(position) 
+ removeDuplicateLetters0(
s.substring(position+1)
.replaceAll("" + s.charAt(position), ""));
    }
}

复杂度分析
时间复杂度:O(N^3), 因为用到了三重循环,最坏情况下时间复杂度达到了N^3。(超出运行时间限制)
空间复杂度:O(N),每次给字符串切片都会创建一个新的字符串(字符串不可变),切片的数量受常数限制,最终复杂度为 O(N) * C = O(N)。

方法二:贪心策略改进

我们发现,对于“是否重复出现”的判断,每次都要偏离当前字母之后的所有字符,这显然做了很多重复工作。
优化的方法,我们可以用一个count数组,保存所有26个字母在s中出现的频次。当我们遍历字符串时,每遇到一个字母,就让它对应的count减一;当当前字母对应的count减为0时,说明之后不会再重复出现了,因此即使有更小的字母也不能替代它,我们直接就可以把它作为最左侧字母输出了。

public String removeDuplicateLetters(String s) {
        if (s.length() == 0) return "";
        int position = 0;
        // 定义一个count数组,用来保存26个字母在s中出现的频次
        int[] count = new int[26];
        for (int i = 0; i < s.length(); i++){
            count[s.charAt(i) - 'a'] ++; 
        }
        for (int i = 0; i < s.length(); i++){
            if (s.charAt(i) < s.charAt(position)) {
                position = i; 
            }
            if (--count[s.charAt(i) - 'a'] == 0){
                break; 
            }
        }
        return s.charAt(position) + 
removeDuplicateLetters(s.substring(position+1)
.replaceAll("" + s.charAt(position), ""));
}

复杂度分析
时间复杂度:O(N)。 每次递归调用占用 O(N) 时间。递归调用的次数受常数限制(只有26个字母),最终复杂度为 O(N) * C = O(N)。

空间复杂度:O(N),每次给字符串切片都会创建一个新的字符串(字符串不可变),切片的数量受常数限制,最终复杂度为 O(N) * C = O(N)。

方法三:贪心策略(用栈实现)

上面的方法由于递归的时候,用到了字符串切片的方法,导致必须要有线性的空间复杂度,而且运行时间也并不短。那还没有别的优化方法呢?
这就需要结合其它的数据结构了。我们可以用栈来存储最终返回的字符串。

代码实现如下:

public String removeDuplicateLetters(String s) {
    Stack<Character> stack = new Stack<>();
    HashSet<Character> seen = new HashSet<>();
    HashMap<Character, Integer> last_occur = new HashMap<>();
    for (int i = 0; i < s.length(); i++){
        last_occur.put(s.charAt(i), i); 
    }
    // 遍历字符串,判断是否入栈
    for (int i = 0; i < s.length(); i++){
        char c = s.charAt(i);
        if (!seen.contains(c)){
            while (!stack.isEmpty() &&
                    c < stack.peek() &&
                    last_occur.get(stack.peek()) > i){
                seen.remove(stack.pop());
            }
            seen.add(c);
            stack.push(c);
        }
    }
    // 将栈中元素保存成字符串输出
    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),但实际上并不是。首先,seen 中字符不重复,其大小会受字母表大小的限制,所以是O(1)。其次,只有 stack 中不存在的元素才会被压入,因此 stack 中的元素也唯一。所以最终空间复杂度为 O(1)。

方法四

其实通过栈还有更加容易理解的解题思路,具体代码如下

class Solution {
    public String removeDuplicateLetters(String s) {
            Stack<Character> stack = new Stack<>();
            
            for(int i =0;i<s.length();i++){
                char c = s.charAt(i);
                if(stack.contains(c)){
                    continue;
                }
                //栈不为空,并且 当前字母比栈中字母更小,并且从当前位置开始往后还有栈中的字母存在,那么就弹出
                while(!stack.isEmpty()&& c<stack.peek()&& s.indexOf(stack.peek(),i)!=-1){
                    stack.pop();
                }
                stack.push(c);
            }
            char[] charArr = new char[stack.size()];
            for(int i=0;i<stack.size();i++){
                charArr[i] = stack.get(i);
            }
            return new String(charArr);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/596661.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

谁能取代迈巴赫,征服互联网安全大佬周鸿祎?

‍作者 |老缅 编辑 |德新 4月18日&#xff0c;「周鸿祎卖车」登上了微博热搜。这位360创始人、董事长发微博称&#xff1a;自己做了一个艰难的决定&#xff0c;将把陪伴9年的迈巴赫600给卖掉。 随后&#xff0c;他解释道&#xff1a;「这是因为我需要体验新一代车的感觉。古人…

SQL注入——绕过information

衔接上文&#xff0c;进一步对SQL注入less-1进行禁止information的操作&#xff0c;上文连接如下&#xff1a; SQL注入less-1-CSDN博客 一、对less-1进行编辑 增加一段代码&#xff0c;作用是禁止information字段 二、进行检查 可以看到代码已经生效&#xff0c;禁止用infor…

TypeError报错处理

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 一、Python中的TypeError简介 这个错误通常表示在方法调用时&#xff0c;参数类型不正确&#xff0c;或者在对字符串进行格式化操作时&#xff0c;提供的变量与预期不符。 二、错误的源头&#xff1a;字符串格式化…

调用第三方接口——支付宝付款

沙箱环境是支付宝开放平台为开发者提供的用于接口开发及主要功能联调的模拟环境。 参考 登录 - 支付宝 在沙箱环境下&#xff0c;已经分配好了用于模拟测试的应用信息、商家信息、买家信息等 小程序文档 - 支付宝文档中心 内网穿透&#xff08;支付宝付款需要在公网进行检查…

MybatisPlus也能轻松生成三层架构代码?

&#x1f469;&#x1f3fd;‍&#x1f4bb;个人主页&#xff1a;阿木木AEcru &#x1f525; 系列专栏&#xff1a;《Docker容器化部署系列》 《Java每日面筋》 &#x1f4b9;每一次技术突破&#xff0c;都是对自我能力的挑战和超越。 目录 一、前言三层架构的流程图为什么使用…

为什么需要自动化测试?自动化有哪些优势?

前言 自动化测试&#xff0c;最近些年可谓是大火。招聘上的要求也好&#xff0c;培训班的广告也罢&#xff0c;比比皆是&#xff0c;足以说明它在业内的火爆程度。 虽然说会写自动化测试并不能说明你就很牛批&#xff0c;但是你不会的话&#xff0c;那么很抱歉&#xff0c;你…

保持 Hiti 证卡打印机清洁的重要性和推荐的清洁用品

在证卡印刷业务中&#xff0c;保持印刷设备的清洁至关重要。特别是对于 Hiti 证卡打印机来说&#xff0c;它们是生产高质量证卡的关键工具。保持设备清洁不仅可以保证打印质量和效率&#xff0c;还可以延长其使用寿命。本文将探讨保持 Hiti 证卡打印机清洁卡的重要性&#xff0…

数码管的显示

静态数码管显示 数码管有两种一种的负电压促发,一种是正电压促发,上图是单数码管的引脚 上图是数码管模组的引脚,采用了引脚复用技术 咱们这个单片机由8个单数码管,所以要用上38译码器,如下图 74138使能端,单片机上电直接就默认接通了 74HC245的作用是稳定输入输出,数据缓冲作…

Rust Course学习(编写测试)

如果友友你的计算机上没有安装Rust&#xff0c;可以直接安装&#xff1a;Rust 程序设计语言 (rust-lang.org)https://www.rust-lang.org/zh-CN/ Introduce 介绍 Testing in Rust involves writing code specifically designed to verify that other code works as expected. It…

线上线下包搭建小程序/公众号/H5 支持二开!

网上交友有以下三个积极影响&#xff1a; 1. 扩展社交圈和增加社交机会&#xff1a;网上交友可以让人们接触到不同地区、不同背景、不同文化的人&#xff0c;拓展人们的社交圈并且增加交友机会。这些新的社交联系对于个人的成长和发展有积极的影响&#xff0c;可以让人们学习新…

奶爸预备 |《P.E.T.父母效能训练:让亲子沟通如此高效而简单:21世纪版》 / 托马斯·戈登——读书笔记

目录 引出致中国读者译序前言第1章 父母总是被指责&#xff0c;而非受训练第2章 父母是人&#xff0c;不是神第3章 如何听&#xff0c;孩子才会说&#xff1a;接纳性语言第4章 让积极倾听发挥作用第5章 如何倾听不会说话的婴幼儿第6章 如何听&#xff0c;孩子才肯听第8章 通过改…

[每日AI·0506]巴菲特谈 AI,李飞飞创业,苹果或将推出 AI 功能,ChatGPT 版搜索引擎

AI 资讯 苹果或将推出 AI 功能&#xff0c;随 iPhone 发布2024 年巴菲特股东大会&#xff0c;巴菲特将 AI 类比为核技术 巴菲特股东大会 5 万字实录消息称 OpenAI 将于 5 月 9 日发布 ChatGPT 版搜索引擎路透社消息&#xff0c;斯坦福大学 AI 领军人物李飞飞打造“空间智能”创…

leetCode75. 颜色分类

leetCode75. 颜色分类 题目思路 代码 class Solution { public:void sortColors(vector<int>& nums) {for(int i 0, j 0, k nums.size() - 1; i < k;){if(nums[i] 0) swap(nums[i],nums[j]);else if(nums[i] 2) swap(nums[i],nums[k--]);else if(nums[i] …

基于Springboot+Vue+Java的学生就业管理系统

&#x1f49e; 文末获取源码联系 &#x1f649; &#x1f447;&#x1f3fb; 精选专栏推荐收藏订阅 &#x1f447;&#x1f3fb; &#x1f380;《Java 精选实战项目-计算机毕业设计题目推荐-期末大作业》&#x1f618; 更多实战项目~ https://www.yuque.com/liuyixin-rotwn/ei3…

Docker容器:Docker-Consul 的容器服务更新与发现

目录 前言 一、什么是服务注册与发现 二、 Docker-Consul 概述 1、Consul 概念 2、Consul 提供的一些关键特性 3、Consul 的优缺点 4、传统模式与自动发现注册模式的区别 4.1 传统模式 4.2 自动发现注册模式 5、Consul 核心组件 5.1 Consul-Template组件 5.2 Consu…

自动驾驶融合定位:IMU内参模型及标定

自动驾驶融合定位&#xff1a;IMU内参模型及标定 一、 概述 标定的本质是参数辨识。首先明确哪些参数可辨识&#xff0c;其次弄清怎样辨识。 参数包括陀螺仪和加速度计各自的零偏、标度因数、安装误差。 辨识就比较丰富了&#xff0c;如果让各位先不局限于标定任务&#xf…

HCIP-Datacom-ARST必选题库_BGP【道题】

1.关于summary automatic命令和BGP聚合的描述,错误的是? 该命令用于实现自动聚合,其优先级高于手动聚合 配置该命令后,BGP将按自然网段聚合路由 该命令用来使能对本地引入的路由进行自动聚合 配置该命令后,BGP只向对等体发送聚合后的路由 1.关于summary automatic命令和BGP聚…

C++初阶学习第五弹——类与对象(下)——类与对象的收官战

类与对象&#xff08;上&#xff09;&#xff1a;C初阶学习第三弹——类与对象&#xff08;上&#xff09;——初始类与对象-CSDN博客 类与对象&#xff08;中&#xff09;&#xff1a;C初阶学习第四弹——类与对象&#xff08;中&#xff09;——刨析类与对象的核心点-CSDN博…

Linux环境下的事件驱动力量:探索Libevent的高性能I/O架构

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的Linux高性能服务器编程系列之《Linux环境下的事件驱动力量&#xff1a;探索Libevent的高性能I/O架构》&#xff0c;在这篇文章中&#xff0c;你将会学习到Libevent的高性能I/O原理以及应用&#xff0c;并且我会给出源码…

云仓酒庄携手央视共筑品牌新高度,酒类行业广告战略迈向新征程

随着酒类市场的日益繁荣与竞争的加剧&#xff0c;品牌宣传与推广在酒类行业中的地位愈发凸显。近日&#xff0c;云仓酒庄宣布与中视中州&#xff08;央视代理机构&#xff09;达成2024-2025年度央视广告战略合作&#xff0c;云仓酒庄副总裁周玄代表云仓酒庄签约&#xff0c;这一…
最新文章