力扣- -去除重复字母

文章目录

  • 力扣- -去除重复字母
    • 一、题目描述
    • 二、分析
    • 三、代码
    • 四、问题描述
    • 五、代码

一、题目描述

二、分析

  • 题目的要求总结出来有三点:

  • 要求一、要去重

  • 要求二、去重字符串中的字符顺序不能打乱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;}
};

力扣- -去除重复字母相关推荐

  1. 20200213:去除重复字母(leetcode316)

    去除重复字母 题目 思路与算法 代码实现 题目 思路与算法 首先对字符串进行遍历,将当前字符串依次入栈, 注意入栈的条件: 新入栈的元素在栈内没有出现过,必须是未出现过的元素 新入栈的元素如果比栈顶元 ...

  2. 316. 去除重复字母

    链接:316. 去除重复字母 题解:https://leetcode-cn.com/problems/remove-duplicate-letters/solution/qu-chu-zhong-fu ...

  3. LeetCode 练习——316. 去除重复字母

    文章目录 1.题目描述 2.思路 2.1 代码 2.2 测试结果 3.总结 1.题目描述 去除重复字母 给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次.需保证 返回结果的字典 ...

  4. LeetCode 316. 去除重复字母 / 1081. 不同字符的最小子序列(单调栈)

    文章目录 1. 题目 2. 解题 1. 题目 LC 316: 给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次.需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置). ...

  5. 316 Remove Duplicate Letters 去除重复字母

    给定一个仅包含小写字母的字符串,去除重复的字母使得所有字母出现且仅出现一次.你必须保证返回结果是所有可能结果中的以字典排序的最短结果. 例如: 给定 "bcabc" 返回 &quo ...

  6. 力扣-无重复字符的最长子串

    给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 " ...

  7. 力扣 187. 重复的DNA序列

    1.题目 DNA序列 由一系列核苷酸组成,缩写为 'A', 'C', 'G' 和 'T'.. 例如,"ACGAATTCCG" 是一个 DNA序列 . 在研究 DNA 时,识别 DN ...

  8. 316. Remove Duplicate Letters 去除重复字母

    给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次.需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置). 注意:该题与 1081 https://leetcode-c ...

  9. leetcode 316. 去除重复字母(单调栈)

    给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次.需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置). 注意:该题与 1081 https://leetcode-c ...

最新文章

  1. SharePoint Server 2010 安装图解
  2. aws python lambda_AWS Lambda
  3. interp3函数-----三维数据插值
  4. VC下调用x264进行视频编码,
  5. 西门子主程序调用子程序_S7200Smart 子程序局部变量使用教程
  6. mysql galera status_MySQL galera cluster集群的监控
  7. Linux下tensorflow1.13.1 C++ API的编译和使用
  8. 微软紧急更新 Windows 8.1 和 Server 2012 R2,修复两个严重漏洞
  9. Python 标准库 csv —— csv 文件的读写
  10. onclick与addEventListener的区别
  11. 【时间序列预测】基于matlab最小均方(LMS)算法时间序列预测【含Matlab源码 1335期】
  12. 【Q】CentOS-部署后,只有本机可以访问的问题
  13. 能翻译整篇论文的软件有什么?
  14. 基于随机森林模型的心脏病患者预测及可视化(pdpbox、eli5、shap、graphviz库)附相关库安装教程
  15. Dell+win10 进入Bios界面设定u盘启动详细图解
  16. isFile()exists() isDirectory()
  17. Spring的第二次模拟考试
  18. 景区在线售票系统解决方案
  19. cad指北针lisp_auto cad指北针图块下载|CAD指北针
  20. 权威机构统计:2021 年最佳数据中心网络公司,中国华为和H3C上榜

热门文章

  1. php 进行http请求,PHP模拟http请求的方法详解
  2. 【报错笔记】maven项目启动时报错
  3. 小技巧:with用法 pycharm控制台输出带颜色的文字 打印进度条的
  4. nginx平滑升级添加ssl实现站内https
  5. 关于子网划分的几个捷径
  6. [转载] 启用和禁用 Reporting Services 的客户端打印和导出文件格式
  7. 华尔街弃儿:雷曼兄弟158岁被清算
  8. Active Directory边界
  9. C++debug调试出现heap corruption detected: after normal block 可能的原因
  10. HDU - 6184 Counting Stars(思维+三元环)