去除重复字母(栈和哨兵)

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

示例
输入: "cbacdcbc"
输出: "acdb"
首先解释一下字典序。字典序是指从前到后比较两个字符串大小的方法

首先比较第 1 个字符,如果不同则第 1 个字符较小的字符串更小;
如果相同则继续比较第 2 个字符 …… 如此继续,比较整个字符串的大小。
观察示例 1:bcabc。

字符 a 在字符串中只出现一次,根据题目要求,字符 a 必须被选取;
字符 b 出现了两次,显然选择 a后面的那个,因为字典序 ab 在 ba 前面。同理,有两个相同的字符 c ,我们选择后一个。因此,输出就是 abc。
再观察示例 2:cbacdcbc。

有 4 个字符:a、b、c、d。其中 a 和 d 只出现一次,必须被选取;
b 出现 2 次,一个在 a 前面,一个在 a 后面,显然保留在 a 后面的;
c 出现 4 次,我们把几种可能都列出来观察一下:
情况 1:cadb
情况 2:acdb(字典序最小)
情况 3:adcb
情况 4:adbc

一种最理想的情况是:abcd,在遍历的时候,遇到的字符串的 ASCII 值逐渐增大。

下面我们就思考,当遍历到的字符的 ASCII 值减少的时候,应该如何处理。

还看示例 1:已经读到了 bc,已经是字典序最小的排列。

即将读到的 a 比 c 的 ASCII 值小。如果 a 能排在 c 之前,就能得到一个比 ca 更小的字典序 ac。

那么 a 能不能排在 c 之前,就看 a 的后面还会不会出现字符 c,显然会。同理,由于字符 b 在将来还会出现,构成的字典序更小,因此舍弃之前的字符 b。

到此为止,应该想到我们需要借助栈帮助我们完成这题。

然后根据这个思路,我们再看一下示例 2:cbacdcbc。

栈.png

第 1 步:读到 c,入栈,此时栈中元素 [c];

第 2 步:读到 b,b 比之前 a 小,c 在以后还会出现,因此 c 出栈,b 入栈,此时栈中元素 [b];

第 3 步:读到 a,a 比之前 b 小,b 在以后还会出现,因此 b 出栈,a 入栈,此时栈中元素 [a];

第 4 步:读到 c,c 比之前 a 大,直接让 c 入栈,此时栈中元素 [a, c];

第 5 步:读到 d,d 比之前 d 大,直接让 d 入栈,此时栈中元素 [a, c, d];

第 6 步:读到 c,这里要注意:此时栈中已经有 c 了,此时栈中元素构成的字符顺序就是最小的字典序,不可能舍弃之前的 c,而用现在的读到的 c,因此这个 c 不需要,直接跳过;

第 7 步:读到 b,b 比之前的 c 小,但是,后面不会再出现 b 了,因此 b 就应该放在这个位置,因此让 b 入栈,此时栈中元素 [a, c, d, b];

第 8 步:读到 c,同第 6 步,这个 c 我们不需要。

于是,我们可以设计如下算法:

1、遍历字符串里的字符,如果读到的字符的 ASCII 值是升序,依次存到一个栈中;
2、如果读到的字符在栈中已经存在,这个字符我们不需要;
3、如果读到的 ASCII 值比栈顶元素严格小,看看栈顶元素在后面是否还会出现,如果还会出现,则舍弃栈顶元素,而选择后出现的那个字符,这样得到的字典序更小。

因为需要判断读到的字符在栈中是否已经存在,因此可以使用哈希表,又因为题目中说,字符只会出现小写字母,用一个布尔数组也是可以的,注意在出栈入栈的时候,需要同步更新一下这个布尔数组。
又因为要判断栈顶元素在后面是否会被遍历到,因此我们需要先遍历一次字符,存一下这个字符最后出现的位置,就能判断栈顶元素在后面是否会被遍历到。

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/remove-duplicate-letters/solution/zhan-by-liweiwei1419/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Java代码

class Solution {
    public String removeDuplicateLetters(String s) {
        int len = s.length();
        if (len < 2) return s;

        // 记录是否在已经得到的字符串中
        boolean[] set = new boolean[26];
        // 记录每个字符出现的最后一个位置
        int[] lastAppearIndex = new int[26];
        for(int i = 0;i < len;i++) {
            lastAppearIndex[s.charAt(i) - 'a'] = i;
        }        

        Deque<Character> stack = new ArrayDeque<>();
        // 此时 `a` 作为哨兵,这个 `a` 永远不会被弹出
        // 如此一来,在遍历的时候,就不用判断栈是否为空了
        stack.push('a');

        for(int i = 0;i < len;i++) {
            char curChar = s.charAt(i);
            if(set[curChar - 'a']) continue;

            while(stack.peek() > curChar && lastAppearIndex[stack.peek() - 'a'] >= i) {
                char top = stack.pop();
                set[top - 'a'] = false;
            }

            stack.push(curChar);
            set[curChar - 'a'] = true;
        }
        
        int size = stack.size();
        StringBuilder result = new StringBuilder();
        for(int i = 0;i < size - 1;i++) {
            result.insert(0, stack.pop());  //这里用append会出错,暂时懒得查append和insert的区别了
        }

        return result.toString();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,082评论 5 464
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,231评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,047评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,977评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,893评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,976评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,605评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,888评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,906评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,732评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,513评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,980评论 3 301
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,132评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,447评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,027评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,232评论 2 339