日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

力扣- -去除重复字母

發布時間:2024/4/11 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 力扣- -去除重复字母 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

力扣- -去除重復字母

文章目錄

  • 力扣- -去除重復字母
    • 一、題目描述
    • 二、分析
    • 三、代碼
    • 四、問題描述
    • 五、代碼

一、題目描述

二、分析

  • 題目的要求總結出來有三點:

  • 要求一、要去重。

  • 要求二、去重字符串中的字符順序不能打亂s中字符出現的相對順序。

  • 要求三、在所有符合上一條要求的去重字符串中,字典序最小的作為最終結果。

上述三條要求中,要求三可能有點難理解,舉個例子:

  • 比如說輸入字符串s = “babc”,去重且符合相對位置的字符串有兩個,分別是"bac"和"abc",但是我們的算法得返回"abc",因為它的字典序更小。
  • 按理說,如果我們想要有序的結果,那就得對原字符串排序對吧,但是排序后就不能保證符合s中字符出現順序了,這似乎是矛盾的。
  • 我們先暫時忽略要求三,用「棧」來實現一下要求一和要求二:
String removeDuplicateLetters(String s) {// 存放去重的結果Stack<Character> stk = new Stack<>();// 布爾數組初始值為 false,記錄棧中是否存在某個字符// 輸入字符均為 ASCII 字符,所以大小 256 夠用了boolean[] inStack = new boolean[256];for (char c : s.toCharArray()) {// 如果字符 c 存在棧中,直接跳過if (inStack[c]) continue;// 若不存在,則插入棧頂并標記為存在stk.push(c);inStack[c] = true;}StringBuilder sb = new StringBuilder();while (!stk.empty()) {sb.append(stk.pop());}// 棧中元素插入順序是反的,需要 reverse 一下return sb.reverse().toString(); }
  • 這段代碼的邏輯很簡單吧,就是用布爾數組inStack記錄棧中元素,達到去重的目的,此時棧中的元素都是沒有重復的。

  • 如果輸入s = “bcabc”,這個算法會返回"bca",已經符合要求一和要求二了,但是題目希望要的答案是"abc"對吧。

  • 那我們想一想,如果想滿足要求三,保證字典序,需要做些什么修改?

  • 在向棧stk中插入字符'a'的這一刻,我們的算法需要知道,字符'a'的字典序和之前的兩個字符'b'和'c'相比,誰大誰小?

  • 如果當前字符'a'比之前的字符字典序小,就有可能需要把前面的字符 pop 出棧,讓'a'排在前面,對吧?

那么,我們先改一版代碼:

String removeDuplicateLetters(String s) {Stack<Character> stk = new Stack<>();boolean[] inStack = new boolean[256];for (char c : s.toCharArray()) {if (inStack[c]) continue;// 插入之前,和之前的元素比較一下大小// 如果字典序比前面的小,pop 前面的元素while (!stk.isEmpty() && stk.peek() > c) {// 彈出棧頂元素,并把該元素標記為不在棧中inStack[stk.pop()] = false;}stk.push(c);inStack[c] = true;}StringBuilder sb = new StringBuilder();while (!stk.empty()) {sb.append(stk.pop());}return sb.reverse().toString(); }
  • 這段代碼也好理解,就是插入了一個 while 循環,連續 pop 出比當前字符小的棧頂字符,直到棧頂元素比當前元素的字典序還小為止。是不是有點「單調棧」的意思了?

  • 這樣,對于輸入s = “bcabc”,我們可以得出正確結果"abc"了。

  • 但是,如果我改一下輸入,假設s = "bcac",按照剛才的算法邏輯,返回的結果是"ac",而正確答案應該是"bac",分析一下這是怎么回事?

  • 很容易發現,因為s中只有唯一一個'b',即便字符'a'的字典序比字符'b'要小,字符'b'也不應該被 pop 出去。

  • 那問題出在哪里?

  • 我們的算法在stk.peek() > c時才會 pop 元素,其實這時候應該分兩種情況:

  • 情況一、如果stk.peek()這個字符之后還會出現,那么可以把它 pop 出去,反正后面還有嘛,后面再 push 到棧里,剛好符合字典序的要求

  • 情況二、如果stk.peek()這個字符之后不會出現了,前面也說了棧中不會存在重復的元素,那么就不能把它 pop 出去,否則你就永遠失去了這個字符。

  • 回到s = "bcac"的例子,插入字符'a'的時候,發現前面的字符'c'的字典序比'a'大,且在'a'之后還存在字符'c',那么棧頂的這個'c'就會被 pop 掉。

  • while 循環繼續判斷,發現前面的字符'b'的字典序還是比'a'大,但是在'a'之后再沒有字符'b'了,所以不應該把'b'pop 出去。

  • 那么關鍵就在于,如何讓算法知道字符'a'之后有幾個'b'有幾個'c'呢?

也不難,只要再改一版代碼:

三、代碼

String removeDuplicateLetters(String s) {Stack<Character> stk = new Stack<>();// 維護一個計數器記錄字符串中字符的數量// 因為輸入為 ASCII 字符,大小 256 夠用了int[] count = new int[256];for (int i = 0; i < s.length(); i++) {count[s.charAt(i)]++;}boolean[] inStack = new boolean[256];for (char c : s.toCharArray()) {// 每遍歷過一個字符,都將對應的計數減一count[c]--;if (inStack[c]) continue;while (!stk.isEmpty() && stk.peek() > c) {// 若之后不存在棧頂元素了,則停止 popif (count[stk.peek()] == 0) {break;}// 若之后還有,則可以 popinStack[stk.pop()] = false;}stk.push(c);inStack[c] = true;}StringBuilder sb = new StringBuilder();while (!stk.empty()) {sb.append(stk.pop());}return sb.reverse().toString(); }
  • C++代碼
class Solution { public:string removeDuplicateLetters(string s) {stack<char> stk;// 維護一個計數器記錄字符串中字符的數量// 因為輸入為 ASCII 字符,大小 256 夠用了vector<int> count(256,0);for (int i = 0; i < s.size(); i++) {count[s[i]]++;}vector<bool> inStack(256,false);for (char& c : s) {// 每遍歷過一個字符,都將對應的計數減一count[c]--;if (inStack[c]) continue;while (!stk.empty() && stk.top() > c) {// 若之后不存在棧頂元素了,則停止 popif (count[stk.top()] == 0) {break;}// 若之后還有,則可以 popinStack[stk.top()] = false;stk.pop();}stk.push(c);inStack[c] = true;}string ret;while (!stk.empty()) {ret += stk.top();stk.pop();}reverse(ret.begin(),ret.end());return ret;} };
  • 我們用了一個計數器count,當字典序較小的字符試圖「擠掉」棧頂元素的時候,在count中檢查棧頂元素是否是唯一的,只有當后面還存在棧頂元素的時候才能擠掉,否則不能擠掉。

至此,這個算法就結束了,時間空間復雜度都是 O(N)。

  • 你還記得我們開頭提到的三個要求嗎?我們是怎么達成這三個要求的?

  • 要求一、通過inStack這個布爾數組做到棧stk中不存在重復元素。

  • 要求二、我們順序遍歷字符串s,通過「棧」這種順序結構的 push/pop 操作記錄結果字符串,保證了字符出現的順序和s中出現的順序一致。

這里也可以想到為什么要用「棧」這種數據結構,因為先進后出的結構允許我們立即操作剛插入的字符,如果用「隊列」的話肯定是做不到的。

  • 要求三、我們用類似單調棧的思路,配合計數器count不斷 pop 掉不符合最小字典序的字符,保證了最終得到的結果字典序最小。

當然,由于棧的結構特點,我們最后需要把棧中元素取出后再反轉一次才是最終結果。

四、問題描述


五、代碼

class Solution { public:string smallestSubsequence(string text) {stack<char> sta;vector<int> count(256,0);for(auto& e : text){count[e - '0']++;}vector<bool> instack(256,false);for(auto& e : text){count[e - '0']--;if(instack[e - '0'] == true){continue;}while(!sta.empty() && sta.top() > e){if(count[sta.top() - '0'] == 0){break;}instack[sta.top() - '0'] = false;sta.pop();}sta.push(e);instack[e - '0'] = true;}string ret;while(!sta.empty()){ret += sta.top();sta.pop();}reverse(ret.begin(),ret.end());return ret;} };

總結

以上是生活随笔為你收集整理的力扣- -去除重复字母的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。