力扣- -去除重复字母
力扣- -去除重复字母
文章目录
- 力扣- -去除重复字母
- 一、题目描述
- 二、分析
- 三、代码
- 四、问题描述
- 五、代码
一、题目描述
二、分析
题目的要求总结出来有三点:
要求一、要
去重
。要求二、去重字符串中的
字符顺序不能打乱
s中字符出现的相对顺序。要求三、在所有符合上一条要求的去重字符串中,
字典序最小的作为最终结果
。
上述三条要求中,要求三可能有点难理解,举个例子:
- 比如说输入字符串s = “babc”,去重且符合相对位置的字符串有两个,分别是"bac"和"abc",但是我们的算法得返回"abc",因为它的字典序更小。
- 按理说,如果我们想要有序的结果,那就得对原字符串排序对吧,但是排序后就不能保证符合s中字符出现顺序了,这似乎是矛盾的。
- 我们先暂时忽略要求三,用「栈」来实现一下要求一和要求二:
String removeDuplicateLetters(String s) {// 存放去重的结果Stack<Character> stk = new Stack<>();// 布尔数组初始值为 false,记录栈中是否存在某个字符// 输入字符均为 ASCII 字符,所以大小 256 够用了boolean[] inStack = new boolean[256];for (char c : s.toCharArray()) {// 如果字符 c 存在栈中,直接跳过if (inStack[c]) continue;// 若不存在,则插入栈顶并标记为存在stk.push(c);inStack[c] = true;}StringBuilder sb = new StringBuilder();while (!stk.empty()) {sb.append(stk.pop());}// 栈中元素插入顺序是反的,需要 reverse 一下return sb.reverse().toString();
}
这段代码的逻辑很简单吧,就是
用布尔数组inStack记录栈中元素,达到去重的目的,此时栈中的元素都是没有重复
的。如果输入s = “bcabc”,
这个算法会返回"bca",已经符合要求一和要求二了,但是题目希望要的答案是"abc"
对吧。那我们想一想,如果想满足要求三,保证字典序,需要做些什么修改?
在向栈stk中插入字符'a'的这一刻,我们的算法需要知道,字符'a'的字典序和之前的两个字符'b'和'c'相比,谁大谁小?
如果当前字符'a'比之前的字符字典序小,就有可能需要把前面的字符 pop 出栈,让'a'排在前面,对吧?
那么,我们先改一版代码:
String removeDuplicateLetters(String s) {Stack<Character> stk = new Stack<>();boolean[] inStack = new boolean[256];for (char c : s.toCharArray()) {if (inStack[c]) continue;// 插入之前,和之前的元素比较一下大小// 如果字典序比前面的小,pop 前面的元素while (!stk.isEmpty() && stk.peek() > c) {// 弹出栈顶元素,并把该元素标记为不在栈中inStack[stk.pop()] = false;}stk.push(c);inStack[c] = true;}StringBuilder sb = new StringBuilder();while (!stk.empty()) {sb.append(stk.pop());}return sb.reverse().toString();
}
这段代码也好理解,
就是插入了一个 while 循环,连续 pop 出比当前字符小的栈顶字符,直到栈顶元素比当前元素的字典序还小为止
。是不是有点「单调栈」的意思了?这样,对于输入s = “bcabc”,我们可以得出正确结果"abc"了。
但是,
如果我改一下输入,假设s = "bcac",按照刚才的算法逻辑,返回的结果是"ac",而正确答案应该是"bac",分析一下这是怎么回事
?很容易发现,因为s中只有唯一一个'b',即便字符'a'的字典序比字符'b'要小,字符'b'也不应该被 pop 出去。
那问题出在哪里?
我们的算法在stk.peek() > c时才会 pop 元素,其实这时候应该分两种情况:
情况一、如果stk.peek()这个字符之后还会出现,那么可以把它 pop 出去,反正后面还有嘛,后面再 push 到栈里,刚好符合字典序的要求
。情况二、如果stk.peek()这个字符之后不会出现了,前面也说了栈中不会存在重复的元素,那么就不能把它 pop 出去,否则你就永远失去了这个字符。
回到s = "bcac"的例子,
插入字符'a'的时候,发现前面的字符'c'的字典序比'a'大,且在'a'之后还存在字符'c',那么栈顶的这个'c'就会被 pop 掉
。while 循环继续判断,发现前面的字符'b'的字典序还是比'a'大,但是在'a'之后再没有字符'b'了,所以不应该把'b'pop 出去
。那么关键就在于,
如何让算法知道字符'a'之后有几个'b'有几个'c'
呢?
也不难,只要再改一版代码:
三、代码
String removeDuplicateLetters(String s) {Stack<Character> stk = new Stack<>();// 维护一个计数器记录字符串中字符的数量// 因为输入为 ASCII 字符,大小 256 够用了int[] count = new int[256];for (int i = 0; i < s.length(); i++) {count[s.charAt(i)]++;}boolean[] inStack = new boolean[256];for (char c : s.toCharArray()) {// 每遍历过一个字符,都将对应的计数减一count[c]--;if (inStack[c]) continue;while (!stk.isEmpty() && stk.peek() > c) {// 若之后不存在栈顶元素了,则停止 popif (count[stk.peek()] == 0) {break;}// 若之后还有,则可以 popinStack[stk.pop()] = false;}stk.push(c);inStack[c] = true;}StringBuilder sb = new StringBuilder();while (!stk.empty()) {sb.append(stk.pop());}return sb.reverse().toString();
}
- C++代码
class Solution {public:string removeDuplicateLetters(string s) {stack<char> stk;// 维护一个计数器记录字符串中字符的数量// 因为输入为 ASCII 字符,大小 256 够用了vector<int> count(256,0);for (int i = 0; i < s.size(); i++) {count[s[i]]++;}vector<bool> inStack(256,false);for (char& c : s) {// 每遍历过一个字符,都将对应的计数减一count[c]--;if (inStack[c]) continue;while (!stk.empty() && stk.top() > c) {// 若之后不存在栈顶元素了,则停止 popif (count[stk.top()] == 0) {break;}// 若之后还有,则可以 popinStack[stk.top()] = false;stk.pop();}stk.push(c);inStack[c] = true;}string ret;while (!stk.empty()) {ret += stk.top();stk.pop();}reverse(ret.begin(),ret.end());return ret;}
};
- 我们
用了一个计数器count,当字典序较小的字符试图「挤掉」栈顶元素的时候,在count中检查栈顶元素是否是唯一的,只有当后面还存在栈顶元素的时候才能挤掉,否则不能挤掉。
至此,这个算法就结束了,时间空间复杂度都是 O(N)。
你还记得我们开头提到的三个要求吗?我们是怎么达成这三个要求的?
要求一、
通过inStack这个布尔数组做到栈stk中不存在重复元素
。要求二、
我们顺序遍历字符串s,通过「栈」这种顺序结构的 push/pop 操作记录结果字符串,保证了字符出现的顺序和s中出现的顺序一致
。
这里也可以想到为什么要用「栈」这种数据结构,因为先进后出的结构允许我们立即操作刚插入的字符,如果用「队列」的话肯定是做不到的。
- 要求三、
我们用类似单调栈的思路,配合计数器count不断 pop 掉不符合最小字典序的字符,保证了最终得到的结果字典序最小。
当然,由于栈的结构特点,我们最后需要把栈中元素取出后再反转一次才是最终结果。
四、问题描述
五、代码
class Solution {public:string smallestSubsequence(string text) {stack<char> sta;vector<int> count(256,0);for(auto& e : text){count[e - '0']++;}vector<bool> instack(256,false);for(auto& e : text){count[e - '0']--;if(instack[e - '0'] == true){continue;}while(!sta.empty() && sta.top() > e){if(count[sta.top() - '0'] == 0){break;}instack[sta.top() - '0'] = false;sta.pop();}sta.push(e);instack[e - '0'] = true;}string ret;while(!sta.empty()){ret += sta.top();sta.pop();}reverse(ret.begin(),ret.end());return ret;}
};
力扣- -去除重复字母相关推荐
- 20200213:去除重复字母(leetcode316)
去除重复字母 题目 思路与算法 代码实现 题目 思路与算法 首先对字符串进行遍历,将当前字符串依次入栈, 注意入栈的条件: 新入栈的元素在栈内没有出现过,必须是未出现过的元素 新入栈的元素如果比栈顶元 ...
- 316. 去除重复字母
链接:316. 去除重复字母 题解:https://leetcode-cn.com/problems/remove-duplicate-letters/solution/qu-chu-zhong-fu ...
- LeetCode 练习——316. 去除重复字母
文章目录 1.题目描述 2.思路 2.1 代码 2.2 测试结果 3.总结 1.题目描述 去除重复字母 给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次.需保证 返回结果的字典 ...
- LeetCode 316. 去除重复字母 / 1081. 不同字符的最小子序列(单调栈)
文章目录 1. 题目 2. 解题 1. 题目 LC 316: 给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次.需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置). ...
- 316 Remove Duplicate Letters 去除重复字母
给定一个仅包含小写字母的字符串,去除重复的字母使得所有字母出现且仅出现一次.你必须保证返回结果是所有可能结果中的以字典排序的最短结果. 例如: 给定 "bcabc" 返回 &quo ...
- 力扣-无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 " ...
- 力扣 187. 重复的DNA序列
1.题目 DNA序列 由一系列核苷酸组成,缩写为 'A', 'C', 'G' 和 'T'.. 例如,"ACGAATTCCG" 是一个 DNA序列 . 在研究 DNA 时,识别 DN ...
- 316. Remove Duplicate Letters 去除重复字母
给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次.需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置). 注意:该题与 1081 https://leetcode-c ...
- leetcode 316. 去除重复字母(单调栈)
给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次.需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置). 注意:该题与 1081 https://leetcode-c ...
最新文章
- SharePoint Server 2010 安装图解
- aws python lambda_AWS Lambda
- interp3函数-----三维数据插值
- VC下调用x264进行视频编码,
- 西门子主程序调用子程序_S7200Smart 子程序局部变量使用教程
- mysql galera status_MySQL galera cluster集群的监控
- Linux下tensorflow1.13.1 C++ API的编译和使用
- 微软紧急更新 Windows 8.1 和 Server 2012 R2,修复两个严重漏洞
- Python 标准库 csv —— csv 文件的读写
- onclick与addEventListener的区别
- 【时间序列预测】基于matlab最小均方(LMS)算法时间序列预测【含Matlab源码 1335期】
- 【Q】CentOS-部署后,只有本机可以访问的问题
- 能翻译整篇论文的软件有什么?
- 基于随机森林模型的心脏病患者预测及可视化(pdpbox、eli5、shap、graphviz库)附相关库安装教程
- Dell+win10 进入Bios界面设定u盘启动详细图解
- isFile()exists() isDirectory()
- Spring的第二次模拟考试
- 景区在线售票系统解决方案
- cad指北针lisp_auto cad指北针图块下载|CAD指北针
- 权威机构统计:2021 年最佳数据中心网络公司,中国华为和H3C上榜
热门文章
- php 进行http请求,PHP模拟http请求的方法详解
- 【报错笔记】maven项目启动时报错
- 小技巧:with用法 pycharm控制台输出带颜色的文字 打印进度条的
- nginx平滑升级添加ssl实现站内https
- 关于子网划分的几个捷径
- [转载] 启用和禁用 Reporting Services 的客户端打印和导出文件格式
- 华尔街弃儿:雷曼兄弟158岁被清算
- Active Directory边界
- C++debug调试出现heap corruption detected: after normal block 可能的原因
- HDU - 6184 Counting Stars(思维+三元环)