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 1:
Input:"bcabc"
Output:"abc"
Example 2:
Input:"cbacdcbc"
Output:"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的大數據,可能實現方法不對吧。。。
參考資料:
https://leetcode.com/problems/remove-duplicate-letters/
https://leetcode.com/discuss/75529/c-simple-solution-easy-understanding
https://leetcode.com/discuss/73761/a-short-o-n-recursive-greedy-solution