Remove Duplicate Letters 刪除重複元素

一笑照夜發表於2020-10-12

一、概述

輸入一個字串,刪除其中重複的字元,並保證剩餘的元素排列是所有可能性中最小的。
這什麼意思?
以cbacdcbc為例,刪完重複的,剩下abcd四個字元,那麼我可以這樣刪:
c b a c d c b c
c b a x d x x x
結果就是cbad
也可以這樣刪
x x a c d x b x
結果就是acdb,這個字典序比cbad小,要這個。

二、分析

麻煩。很複雜。我自己分析的貪心演算法都只能找到區域性最優,找不到全域性最優。
先想一個問題,我們只遍歷一遍的話,如何判斷當前的字元放不放到結果中?
以字串cbacdcbc為例
首先是c,放入結果,結果為c
然後是b,放入結果,結果為cb
然後是a,放入結果,結果為cba
然後是c,結果中已經有c了,那這兩個c要哪個呢?要前面那個是cba,後邊這個是bac,那肯定要後邊這個不要前邊這個
那我這麼看太麻煩了不說,還有這種情況:
對於字串bcabc
首先是bca,結果是bca,
然後是b,結果中已經有b了,那要之前的b還是這個b?要之前的b,結果是bca,這個b,結果是cab,要之前的。
然後是c,結果中已經有c了,那要之前的c還是這個c?要之前的c,結果是bca,這個c,結果是bac,要這個。
所以結果是bac。結果錯了。
應該是abc。為什麼?因為我們應該要第二個b而不是第一個。
也就是說,不能只遍歷一次,只遍歷一次只能得到區域性最優,無法得到全域性最優。

正確演算法是遍歷兩次。第一次知道各個字元的儲量。仍然以cbacdcbc為例。
先遍歷一次探明儲量。a有一個,b有2,c有4,d有1。
然後遍歷第二次:
首先是c,放入結果,結果為c
然後是b,先看結果,結果中有c,現在有b,是委屈一下b,把b放在c後面,結果是bc,還是不要這個c了,結果仍然是b?
也就是說,這個c到底要不要?那得看後面有沒有c,後面還有c,那麼必定最後有結果…b…c;後面沒有c,那麼這個獨苗就得保住。之前已經探明瞭儲量,有c,那麼這個c就滾蛋,目前結果是b。
總結起來就是:保這個c,結果是cb…,不保,結果是b…c…。我們要字典序最小,那麼肯定要後者。
之後是a。同理,由於b比a大,是委屈這個a,結果是ba,還是不要這個b,結果是a。看b的儲量。b有倆,那不客氣了,b滾蛋
結果為a
之後是c,尾巴是a,比c小,放入結果,結果是ac。
之後是d,尾巴是c,放入結果,結果是acd。
之後是c,結果中已經有c了,那麼這個c就不用看了。因為結果中一定是當前最優的。
之後是b,尾巴是d,比b大,想刪,只有一個,刪不了,那b就只能放在最後,結果是acdb。
以此類推。
總結一下:先探明各個字元的儲量。然後遍歷一次想得到結果。就得拿捏好到底要哪個字元。我們要求字元序越小越好,也就是說小的字元越在前越好。
那麼,每當遇到一個結果中沒有的字元,就從最後一個當前的結果開始比,最後一個字元比當前字元大,儲量還有,那麼就把它刪了,否則就把當前字元放在最後。
核心就是b…c…一定比cb好。那麼我就把cb這個c撇了,反正之後還有。優化結果更重要。
先保證有無,再保證字典序最小。

三、總結

有點暈,這個演算法不太好想,得明確貪心的規則:
當前字元在結果中已存在?
是,跳過;否,結尾字元儲量還有?
否,當前字元放在最後;是,當前字元比最後一個字元小?
否,當前字元放到最後;是,刪除最後一個,繼續和最後一個比較。
PS:程式碼如下:

class Solution {
public:
    string removeDuplicateLetters(string s) {
        
        string res="";
        vector<int> HT(256,0);
        for(auto i:s)
            HT[i]++;
        for(auto i:s)
        {
            HT[i]--;
            if(res=="")
            {
                res+=i;
                continue;
            }
            else if(res.find(i)!=string::npos)
                continue;
            else
            {
                while(res!=""&&HT[res[res.size()-1]]>0&&res[res.size()-1]>i)
                    res.pop_back();
                if(res.find(i)==string::npos)
                    res+=i;
            }
        }
        return res;
    }
}; 

相關文章