1. 程式人生 > >[LeetCode] Remove Duplicate Letters 移除重複字母

[LeetCode] Remove Duplicate Letters 移除重複字母

Given a string which contains only lowercase letters, remove duplicate letters so that every letter appear once and only once. You must make sure your result is the smallest in lexicographical order among all possible results.

Example:

Given "bcabc"
Return "abc"

Given "cbacdcbc"
Return "acdb"

Credits:
Special thanks to 

@dietpepsi for adding this problem and creating all test cases.

這道題讓我們移除重複字母,使得每個字元只能出現一次,而且結果要按字母順序排,前提是不能打亂其原本的相對位置。我們的解題思路是:先建立一個雜湊表來統計每個字母出現的次數,還需要一個visited數字來紀錄每個字母是否被訪問過,我們遍歷整個字串,對於遍歷到的字元,先在雜湊表中將其值減一,然後看visited中是否被訪問過,若訪問過則繼續迴圈,說明該字母已經出現在結果中並且位置已經安排妥當。如果沒訪問過,我們和結果中最後一個字母比較,如果該字母的ASCII碼小並且結果中的最後一個字母在雜湊表中的值不為0(說明後面還會出現這個字母),那麼我們此時就要在結果中刪去最後一個字母且將其標記為未訪問,然後加上當前遍歷到的字母,並且將其標記為已訪問,以此類推直至遍歷完整個字串s,此時結果裡的字串即為所求。這裡有個小技巧,我們一開始給結果字串res中放個"0",就是為了在第一次比較時方便,如果為空就沒法和res中的最後一個字元比較了,而‘0’的ASCII碼要小於任意一個字母的,所以不會有問題。最後我們返回結果時再去掉開頭那個‘0’即可,參見程式碼如下:

class Solution {
public:
    string removeDuplicateLetters(string s) {
        int m[256] = {0}, visited[256] = {0};
        string res = "0";
        for (auto a : s) ++m[a];
        for (auto a : s) {
            --m[a];
            if (visited[a]) continue;
            while (a < res.back() && m[res.back()]) {
                visited[res.back()] 
= 0; res.pop_back(); } res += a; visited[a] = 1; } return res.substr(1); } };

這道題如果用Java解題的話可以使用一種遞迴的方法,參見這裡。思路是:先用雜湊表記錄每個字母出現的次數,再遍歷給定字串s,找出最小的字母,每比較一個字母,在雜湊表中的值減1,如果此時為0了,則不繼續遍歷了,此時我們記錄了一個位置,把字串s中該位置左邊的字元都刪掉,右邊的所有再出現的該字母也刪掉,遞迴呼叫此函式即可,在Java中可以使用replaceAll函式,我用STL自己寫了一個無法通過OJ的大資料,可能實現方法不對吧。。。

參考資料: