Leetcode - Remove Duplicate Letters

My code:

public class Solution {
    public String removeDuplicateLetters(String s) {
        if (s == null || s.length() == 0)
            return s;
        int len = s.length();
        Stack<Character> tracker = new Stack<Character>();
        HashMap<Character, Integer> dic = new HashMap<Character, Integer>(); // character -> numbers of this character
        /** form this dictionary to record the numbers of characters */
        for (int i = 0; i < len; i++) {
            char curr = s.charAt(i);
            if (!dic.containsKey(curr))
                dic.put(curr, 1);
            else {
                int times = dic.get(curr);
                dic.put(curr, times + 1);
            }
        }
        /** remove elements from string */
        for (int i = 0; i < len; i++) {
            char curr = s.charAt(i);
            if (tracker.isEmpty())
                tracker.push(curr);
            else if (tracker.contains(curr)) {
                int times = dic.get(curr);
                dic.put(curr, times - 1);
                continue;
            }
            else {
                while (!tracker.isEmpty()) {
                    int times = dic.get(tracker.peek());
                    if (curr < tracker.peek() && times > 1) {
                        dic.put(tracker.peek(), times - 1);
                        tracker.pop();
                    }
                    else {
                        tracker.push(curr);
                        break;
                    }
                }
                if (tracker.isEmpty())
                    tracker.push(curr);
            }
        }
        StringBuilder ret = new StringBuilder();
        while (!tracker.isEmpty()) {
            ret.append(tracker.pop());
        }
        return ret.reverse().toString(); // string does not have reverse() method
    }
}

這道題木我沒有做出來。看了答案才想出來。
一開始我寫了我的解法,就是看右邊的值是否比自己小。小的化就刪除自己。后來發(fā)現(xiàn),右邊第一個(gè)可能比自己大,但是右邊第二個(gè)比自己小的時(shí)候,依然得把自己刪除。然后我就不知道怎么繼續(xù)寫了。看了答案。拿Stack來做。很巧妙。
首先,為什么拿Stack來做呢?他的什么特性復(fù)合這道題木呢?
我們需要拿后面的字母和前面的作比較,進(jìn)行操作,而不需要拿前面的字母和后面的字母作比較。所以,棧很合適。
做法是,先用HashMap做一個(gè)字典。統(tǒng)計(jì)每個(gè)字母出現(xiàn)的個(gè)數(shù)。
然后遍歷整個(gè)字符串。
當(dāng)棧為空的時(shí)候,直接插入。
非空時(shí),
當(dāng)插入的元素,棧中已經(jīng)存在了,那么就舍棄,直接continue;
當(dāng)插入的元素,比棧頂元素值要小時(shí),判斷該棧頂元素的對(duì)應(yīng)counter是否大于1,如果大于1,說明以后還會(huì)出現(xiàn)。那么,就把該元素pop出來。然后拿插入元素和新的棧頂元素比較,進(jìn)行相同操作。直到最后棧空或者棧頂元素小于該插入元素時(shí),就向棧插入該元素。
這一步很關(guān)鍵,他保證了,棧中的元素,從底部到頂部,值是從小到大的。除非某些元素一共只出現(xiàn)了一次,那沒辦法。
然后新的插入元素出現(xiàn)時(shí),如果棧頂元素,即他左側(cè)的字母,是可以刪除的(counter > 1), 那么我們就該選擇刪除這個(gè)元素而不是后面的,因?yàn)檫@樣的話,值小的元素就能移動(dòng)到前端,滿足字符順序。
如果插入元素已經(jīng)存在在了棧中,那么丟棄該元素,也就是刪除該字母而不是前面的相同字母。為什么。因?yàn)闂J菑拇蟮叫∨帕械模丝痰淖址帜甘菑男〉酱蟮摹h除了棧中的元素,會(huì)使得字典順序變大。

還有一種解法具體我就不研究了。
參考網(wǎng)頁:
https://www.hrwhisper.me/leetcode-remove-duplicate-letters/

Anyway, Good luck, Richardo!

My code:

import java.util.HashMap;

public class Solution {
    public String removeDuplicateLetters(String s) {
        if (s == null || s.length() == 0) {
            return s;
        }
        
        HashMap<Character, Integer> helper = new HashMap<Character, Integer>();
        for (int i = 0; i < s.length(); i++) {
            char curr = s.charAt(i);
            helper.put(curr, i);
        }
        
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        StringBuilder ret = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char curr = s.charAt(i);
            if (!map.containsKey(curr)) {
                ret.append(curr);
                map.put(curr, ret.length() - 1);
            }
            else {
                int pre = map.get(curr);
                int start = -1;
                int end = pre + 1;
                while (end < ret.length()) {
                    if (ret.charAt(end) > ret.charAt(pre)) {
                        if (find(s, i + 1, ret.charAt(end), helper)) {
                            end++;
                        }
                        else {
                            break;
                        }
                    }
                    else {
                        if (start == -1 || ret.charAt(start) > ret.charAt(end)) {
                            start = end;
                        }
                        if (find(s, i + 1, ret.charAt(end), helper)) {
                            end++;
                        }
                        else {
                            break;
                        }
                    }
                }
                if (start != -1) {
                    StringBuilder temp = new StringBuilder(ret.substring(0, pre));
                    map.clear();
                    for (int j = 0; j < pre; j++) {
                        map.put(ret.charAt(j), j);
                    }
                    for (int j = start; j < ret.length(); j++) {
                        temp.append(ret.charAt(j));
                        map.put(ret.charAt(j), temp.length() - 1);
                    }
                    temp.append(curr);
                    map.put(curr, temp.length() - 1);
                    ret = temp;
                }
            }
        }
        
        return ret.toString();
    }
    
    private boolean find(String s, int start, char target, HashMap<Character, Integer> helper) {
        return helper.get(target) >= start;
    }
    
    public static void main(String[] args) {
        Solution test = new Solution();
        String ret = test.removeDuplicateLetters("mitnlruhznjfyzmtmfnstsxwktxlboxutbic");
        System.out.println(ret);
    }
}

沒想到之前做過這道題目,可我完全沒印象了。。。
然后自己寫了出來,提交了好多次。。。許多細(xì)節(jié)問題沒能考慮到。

先說下我的思想。其實(shí)也是棧的思想,思路起源于這道題目:
http://www.lxweimin.com/p/d6115154c548

他其中的一個(gè)子問題,給定 arr[], k, 取出k個(gè)數(shù)字組成最大的數(shù)字,保留原數(shù)組順序。

然后這道題目很像,就是當(dāng)碰到某些條件需要?jiǎng)h除時(shí),什么情況下,可以刪除。什么情況下,不能刪除。

首先,條件是什么?
條件是,當(dāng)當(dāng)前這個(gè)字符,在之前已經(jīng)出現(xiàn)過了。
ok,然后我們就需要考慮,刪不刪?

我們?nèi)〕鲈址膇ndex,從他后面開始遍歷。
這里有個(gè)細(xì)節(jié),遍歷的是之前掃出來的獨(dú)一無二的string, ret

int pre = map.get(curr);
so end = pre + 1;
while (end < ret.length()) {
....
}

然后對(duì)于遍歷的每個(gè)字符

if (ret.charAt(curr) > ret.charAt(pre)) {
   ...
}
else {
...
}

如果大的話,這個(gè)字符肯定不能刪,但不代表后面不會(huì)出現(xiàn)更小的。
那么我們就需要判斷,這個(gè)較大的字符,如果刪了,后面有沒有補(bǔ)充。
即,在 s[i + 1, s.length() - 1] 的范圍內(nèi),該較大的字符是否仍然存在。
如果是,那么,那么就往后繼續(xù)走,看看有沒有小的。
如果不是,直接跳出循環(huán)。

如果小的話,我們需要考慮 start 的賦值。
如果start == -1, 那么直接賦值。
如果start != -1, 那么,如果當(dāng)前的字符,同樣小于原start指向的字符,那么就更新start,否則,就略過。
然后接著判斷,是否可以繼續(xù)掃下去。原理差不多。

然后就差不多了。

然后看了下以前的解法,用Stack的。真的很簡(jiǎn)潔!
自己重寫了下:
My code:

public class Solution {
    public String removeDuplicateLetters(String s) {
        if (s == null || s.length() == 0) {
            return s;
        }
        
        HashMap<Character, Integer> dic = new HashMap<Character, Integer>();
        for (int i = 0; i < s.length(); i++) {
            char curr = s.charAt(i);
            if (!dic.containsKey(curr)) {
                dic.put(curr, 1);
            }
            else {
                dic.put(curr, dic.get(curr) + 1);
            }
        }
        
        Stack<Character> st = new Stack<Character>();
        for (int i = 0; i < s.length(); i++) {
            char curr = s.charAt(i);
            dic.put(curr, dic.get(curr) - 1);
            if (st.isEmpty()) {
                st.push(curr);
            }
            else if (st.contains(curr)) {
                continue;
            }
            else {
                if (curr > st.peek()) {
                    st.push(curr);
                }
                else {
                    while (!st.isEmpty() && st.peek() > curr && dic.get(st.peek()) > 0) {
                        st.pop();
                    }
                    st.push(curr);
                }
            }
        }
        
        StringBuilder ret = new StringBuilder();
        while (!st.isEmpty()) {
            ret = ret.append(st.pop());
        }
        
        return ret.reverse().toString();
    }
}

Anyway, Good luck, Richardo! -- 08/25/20

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容