算法随笔_30: 去除重复字母

上一篇:算法随笔_29:最大宽度坡_方法3-CSDN博客

=====

题目描述如下:

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

示例 1:

输入:s = "bcabc"
输出"abc"

=====

算法思路:

首先我们考虑第一个条件: 如何去掉字符串中重复的字母?这个比较简单。我们可以新开辟一个同样长度的新数组s_new来存储最后的结果。然后我们从左往右遍历原数组,依次把字符放入新数组s_new中。并判断即将放入的字符在新数组当中是否已经出现,如果出现,则不放入字符。最终得到的就是去掉重复字符之后的新的字符串。在代码实现的时候可能会有一些细节需要考虑,比如说,s_new数组后面可能会出现未填满的情况,但这属于细节问题,在代码实现中可以用各种办法解决它,同时也不会影响时间复杂度。

现在让我们来看第二个条件: 最终答案需要取字典序最小的字符串。比如,示例1中有两种可能的符合去重条件的答案: bca,  abc。同样都是去掉重复字符之后的字符串,但字典序最小的字符串是abc。

因此,在上面的算法中,当发现放入的字符比如: c,在新数组中已经出现时,我们需要一个算法来判断如何进行重复字符的取舍问题。是保留已经在数组中的字符c,还是需要删除它,放入后面的字符c。

我们拿上面的例子做进一步的分析。bcabc,我们从左向右枚举原字符串,当枚举到第二个b时,如果删除最后一个b,那么字符串就变成bca。删除第一个b,字符串就变成了cab。我们发现只要b的后面的字符是大于b的,肯定要删除第二次出现的重复字母。因为如果删除了第一次出现的字符b,字符c就前移一位,不管后面的字符串是什么样的,以字符c开始引领的字符串必然大于以字符b开始引领的同样长度的字符串。

与上面的情况类似,如果b的后面的字符是小于b的字符,那需要删除第一个字符b。比如bab,最后的结果应该是ab。

因此,我们发现的特征就是:

如果s[i]>s[i+1],且s[i]这个字符出现2次及以上时,我们需要删除这个字符s[i]。

此时注意一下,当删除s[i]之后,s[i+1]移到了s[i]这个位置,新的排列仍然需要保持这个特征。即,如果s[i-1]仍然大于s[i+1],且s[i-1]这个字符出现2次及以上时,我们仍需要删除这个字符s[i-1],s[i+1]需要继续前移。

还是用上面的例子说明,当我们尝试放入s_new时,有如下步骤:

- 放入b

- 因为c大于b,所以放入c

- 因为a小于c,且c出现2次,删除c

- a继续和b比较,a小于b,且b出现2次,删除b。前面已经没有可以删除的字符,放入a。

- 因为b大于a,所以放入b

- 因为c大于b,所以放入c,至此完成。

这里有一些细节还需要说明一下。

1. 假如原字符很长,abc后面还有其他字符,且abc每个字符后面都还出现多次以上。仍然需要按照上面的规律来放入s_new。

2.  只出现1次的字符,必须保留。比如上面的例子,如果没有第二次出现的字符c。需要依次放入bca,然后舍弃第二个b。因为字符c不能删除,所以字符a就无需依次和前面的比较了。

3.  即将放入的字符如果在s_new中已经存在,则不能放入。

我们发现s_new中的字符有个特点,除了那些只出现1次的字符,出现2次及以上的字符都是按字典序增大的,然后碰到小于的字符在一个一个删除。这很像一个的数据结构。先递增入栈,在依据条件出栈。

经过上面一系列的分析,我们大体了解了整个的算法思路。下面我们来给出详细的算法:

1. 初始ch2cnt数组,共26个元素。我们用每个字母与字母a的ascii码的差值来做为数组的索引。初始元素值为0。遍历一遍原字符串,相同字母每出现一次,ch2cnt相应的元素值加1。统计出每个字母出现的次数。

2.  初始putted数组,也是26个元素。用每个字母与字母a的ascii码的差值来做为数组的索引。元素值为1表示此字母在s_new中已经存在,0表示不存在。然后把原字符串s中第一个字符在putted中对应的元素置为1。设置此数组的目的是为了更高效的查询s_new中已存在的字符,仅有O(1) 的时间复杂度。

3. 设s_new数组为最终的字符串数组。初始化时放入原字符串s的第一个字符。

4.  从第二个字符开始,从左向右枚举原字符串s。

5.  通过putted,判断当前字符s[i]是否在s_new中已经存在。如果存在,不放入s_new,且在ch2cnt中对应字符的次数减1。转到步骤4继续。如果不存在,转到下一步。

5.  从右往左枚举s_new数组,让s[i]依次与s_new数组的字符j比较,如果s[i]<=s_new[j]且字符j出现的次数大于1,我们去掉s_new的最后一个字符。循环步骤5,直到退出循环,然后我们把s[i]放入s_new。

其他一些细节详见代码。下面是代码实现:

class Solution(object):
    def removeDuplicateLetters(self, s):
        """
        :type s: str
        :rtype: str
        """
        ord_a=ord('a')
        ch2cnt=[]
        putted=[]
        for i in range(26):
            ch2cnt.append(0)
            putted.append(0)
        for ch in s:
            ch2cnt[ord(ch)-ord_a]+=1
        s_len=len(s)
        s_new=[s[0]]
        
        putted[ord(s[0])-ord_a]=1
        for i in range(1,s_len):
            ord_si=ord(s[i])-ord_a
            if putted[ord_si]==1:
                ch2cnt[ord_si]-=1
                continue
                
            j=len(s_new)-1
            while j>=0 and s[i]<=s_new[j] and ch2cnt[ord(s_new[j])-ord_a]>1:
                ch2cnt[ord(s_new[j])-ord_a]-=1
                putted[ord(s_new[j])-ord_a]=0
                s_new.pop()
                j-=1
            
            s_new.append(s[i])
            putted[ord_si]=1
               
        res=''.join(s_new)
        return res

此算法的时间复杂度为O(n) 。

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

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

相关文章

【C++】设计模式详解:单例模式

文章目录 Ⅰ. 设计一个类&#xff0c;不允许被拷贝Ⅱ. 请设计一个类&#xff0c;只能在堆上创建对象Ⅲ. 请设计一个类&#xff0c;只能在栈上创建对象Ⅳ. 请设计一个类&#xff0c;不能被继承Ⅴ. 请设计一个类&#xff0c;只能创建一个对象&#xff08;单例模式&#xff09;&am…

LLM 推理

https://www.bilibili.com/video/BV16yqeYhELh/ 大模型推理加速目标&#xff1a;高吞吐、低延迟 TGI vLLM SGLang LMDeploy 商汤 和 上海人工智能实验室 一起开发 缺点 性能对比 分析总结 https://www.bilibili.com/video/BV16yqeYhELh/ 大模型推理加速目标&#xff1a;高吞吐…

UE(UltraEdit) 配置简易C/C++编译运行环境

该类型其他帖子 EmEditor 配置简易C/C 编译运行环境_emeditor 代码运行-CSDN博客 RJ TextEd 配置简易C/C 编译运行环境-CSDN博客 这种配置适合ACM竞赛&#xff0c;即要求不使用现代IDE&#xff0c;又想用一个比较好用、至少支持代码高亮的编辑器。 前提条件 1.Mingw GCC 已…

XSS 漏洞全面解析:原理、危害与防范

目录 前言​编辑 漏洞原理 XSS 漏洞的危害 检测 XSS 漏洞的方法 防范 XSS 漏洞的措施 前言 在网络安全的复杂版图中&#xff0c;XSS 漏洞&#xff0c;即跨站脚本攻击&#xff08;Cross - Site Scripting&#xff09;&#xff0c;是一类极为普遍且威胁巨大的安全隐患。随着互…

Alfresco Content Services dockerCompose自动化部署详尽操作

Alfresco Content Services docker社区部署文档 Alfresco Content Services简介 Alfresco Content Services&#xff08;简称ACS&#xff09;是一款功能完备的企业内容管理&#xff08;ECM&#xff09;解决方案&#xff0c;主要面向那些对企业级内容管理有高要求的组织。具体…

LCR 139.训练计划 I

目录 题目过程解法双指针法&#xff08;两端开始&#xff09;快慢指针 题目 教练使用整数数组 actions 记录一系列核心肌群训练项目编号。为增强训练趣味性&#xff0c;需要将所有奇数编号训练项目调整至偶数编号训练项目之前。请将调整后的训练项目编号以 数组 形式返回。 过…

AboutDialog组件的功能和用法

文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了AlertDialog Widget相关的内容,本章回中将介绍AboutDialog Widget.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1 概念介绍 我们在这里说的AboutDialog是一种弹出式窗口&#xff0c;和上一章回中介绍的Al…

Redis学习之哨兵二

一、API 1.sentinel masters:展示被监控的主节点状态及相关的统计信息 2.sentinel master <master name>:展示指定的主节点的状态以及相关的统计信息 3.sentinel slaves <master name>:展示指定主节点的从节点状态以及相关的统计信息 4.sentinel sentinels <mas…

03链表+栈+队列(D2_栈)

目录 讲解一&#xff1a;栈 一、基本介绍 二、代码示例 ------------------------------ 讲解二&#xff1a;单调栈 一、基本介绍 二、适用场景 三、情形示例 1. 寻找左边第一个小于它的数 2. 寻找左边第一个小于它的数的下标 3. 寻找右边第一个大于它的数 4. 寻找右…

春晚魔术中的数学知识

蛇年春晚刘谦魔术又和大家普及了一下编程中的冒泡排序法&#xff0c;思考深入一点&#xff0c;它还涉及到群论和组合数学中的一些知识。 游戏规则和操作步骤&#xff0c;任意打乱三种餐具作为初始状态&#xff1a; 1.筷子和左边的东西互换&#xff0c;如果筷子就在左边&#…

OpenCV:开运算

目录 1. 简述 2. 用腐蚀和膨胀实现开运算 2.1 代码示例 2.2 运行结果 3. 开运算接口 3.1 参数详解 3.2 代码示例 3.3 运行结果 4. 开运算应用场景 5. 注意事项 6. 总结 相关阅读 OpenCV&#xff1a;图像的腐蚀与膨胀-CSDN博客 OpenCV&#xff1a;闭运算-CSDN博客 …

基于Springboot的健身房管理系统【附源码】

基于Springboot的健身房管理系统 效果如下&#xff1a; 系统登陆页面 管理员主页面 器材类型管理页面 健身房管理页面 教练管理页面 用户管理页面 个人信息页面 课程管理页面 研究背景 随着健康意识的不断增强和人们生活水平的提高&#xff0c;健身房已经成为了现代城市中不…

扣子平台音频功能:让声音也能“智能”起来。扣子免费系列教程(14)

在数字化时代&#xff0c;音频内容的重要性不言而喻。无论是在线课程、有声读物&#xff0c;还是各种多媒体应用&#xff0c;音频都是传递信息、增强体验的关键元素。扣子平台的音频功能&#xff0c;为开发者和内容创作者提供了一个强大而灵活的工具&#xff0c;让音频的使用和…

初始Python篇(8)—— 异常

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; Python 目录 异常介绍 异常的处理 try-except try-except-else try-except-else-finally 异常的抛出 常见的异常类型 异常介绍 在…

SSM-MyBatis-总结

文章目录 一、Hello MyBatis1.1 流程1.2 总结 二、Crud 的一些注意点三、参数传递3.1 #{ } VS ${ }3.2 单、复参数传递&#xff08;1&#xff09;单参数&#xff08;2&#xff09;多参数 -- Param&#xff08;3&#xff09;总结 四、查询结果返回--结果封装4.1 ResultType 一般…

【算法设计与分析】实验1:字符串匹配问题的算法设计与求解

目录 一、实验目的 二、实验环境 三、实验内容 四、核心代码 五、记录与处理 六、思考与总结 七、完整报告和成果文件提取链接 一、实验目的 给定一个文本&#xff0c;在该文本中查找并定位任意给定字符串。 1、深刻理解并掌握蛮力法的设计思想&#xff1b; 2、提高应用…

10.6.1 文本文件读、写和追加

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 文本文件的读写通常的做法是建立一个与文件关联的filestream&#xff0c;然后使用StreamReader读取或者StreamWriter写入。 为了详…

DevEco Studio 4.1中如何创建OpenHarmony的Native C++ (NAPI)程序

目录 引言 操作步骤 结语 引言 OpenHarmony的开发工具变化很快&#xff0c;有的时候你安装以前的教程进行操作时会发现界面和操作方式都变了&#xff0c;进行不下去了。比如要在OpenHarmony中通过NAPI调用C程序&#xff0c;很多博文&#xff08;如NAPI篇【1】——如何创建含…

达梦拷贝DM_HOME的复制安装

近期一个项目需求&#xff0c;需要在没有安装包的情况下&#xff0c;将达梦数据库安装到虚机上&#xff08;生产机上安装了达梦&#xff09;&#xff0c;故采用直接打包生产机DM_HOME的方式拷贝至虚机&#xff0c;再依次执行达梦的部分指令完成安装。以下为验证的步骤&#xff…

【MySQL】初始MySQL、库与表的操作

目录 基本使用 使用案例 SQL分类 存储引擎 库的操作 字符集和校验规则 查看系统默认字符集和校验规则 查看数据库支持的字符集 查看数据库支持的字符集校验规则 指定编码常见数据库 校验规则对数据库的影响 操纵数据库 库的备份与恢复 表的操作 创建表 查看表 …