去除重複字母(不同字元的最小序列)問題

Grey Zeng發表於2022-07-10

去除重複字母(不同字元的最小序列)問題

作者:Grey

原文地址:去除重複字母(不同字元的最小序列)問題

題目描述

LeetCode 316. Remove Duplicate Letters

LeetCode 1081. Smallest Subsequence of Distinct Characters

思路

由於題目說明了,字串都是小寫字母,所以第一步,

我們可以通過設定一個含有26個元素的陣列來統計每個字元出現的頻率

        char[] str = s.toCharArray();
        int[] map = new int[26];
        for (char c : str) {
            map[c - 'a']++;
        }

通過如上方法,就可以得到每個字元的詞頻,

第二步,我們設定兩個指標

int l = 0;
int r = 0;

先移動r指標,r每次移動一個位置,就把這個位置字元的詞頻減少一個,如果r某個時刻來到i位置,i位置的字元詞頻減少一個後就沒有了,這就說明從[l...r]區間可以找到一個最小ASCII的字元開始結算了,假設[l...r]的最小ASCII字元在p位置,則我們需要做如下處理:

第一步: 將p位置的字元加入結果字串中。

第二步:將p位置的詞頻設定為-1,便於標識p位置的字元已經使用過了,下次再遇到可以直接跳過:map[str[p]-'a']=-1

第三步:將[p+1...r]的字元詞頻重新加1,因為這部分的字元是減多了的。

第四步:rl都來到p+1位置,繼續上述處理流程。

示例圖如下:

原始串和詞頻表如下:

image

接下來r位置字元的詞頻減少一個,r來到下一個位置

image

此時沒有任何詞頻即將減少為0,繼續移動r

image

接下來移動r,注:此時d的詞頻即將減少到0,

image

接下來,就開始收集第一個元素,即在[l...r]區間內,找到ASCII最小的元素,即2號位置的a元素,此時,將a收集到結果字串中,並且記錄下a此時位置,即p=2

然後,我們需要把整個字串中a的詞頻設定為-1,表示a字元已經收集過了,即
map['a'-'a']=-1

最後,由於a位置是收集到的位置,而r已經遍歷到了3號位置的d元素,所以從a所在的位置到r位置中的所有元素,都要重新加一次詞頻(因為每次移動r會刪除詞頻)。

完整程式碼如下:

    public static String removeDuplicateLetters(String s) {
        char[] str = s.toCharArray();
        int[] map = new int[26];
        for (char c : str) {
            map[c - 'a']++;
        }
        StringBuilder sb = new StringBuilder();
        int l = 0;
        int r = 0;
        while (r < str.length) {
            if (map[str[r] - 'a'] == -1 || --map[str[r] - 'a'] > 0) {
                // r在一個已經處理過的位置或者r上的詞頻減掉後沒有到0,說明現在
                // 還沒有來到需要統計的時刻
                // r放心++
                r++;
            } else {
                // r位置的詞頻已經減少到0了
                // 可以結算了
                int p = l;
                for (int i = l; i <= r; i++) {
                    if (map[str[i] - 'a'] != -1 && str[i] < str[p]) {
                        p = i;
                    }
                }
                if (map[str[p] - 'a'] != -1) {
                    // 結算的位置必須是有效位置
                    sb.append(str[p]);
                    // 結算完畢後,將這個位置的詞頻設定為-1,便於後續判斷此位置是否已經被用過
                    map[str[p] - 'a'] = -1;
                }
                for (int i = p + 1; i <= r; i++) {
                    // [p+1,r]之間的位置的字元,把詞頻還原回來,因為這部分詞頻是減多了的
                    if (map[str[i] - 'a'] != -1) {
                        map[str[i] - 'a']++;
                    }
                }
                l = p + 1;
                r = l;
            }
        }
        return sb.toString();
    }

更多

演算法和資料結構筆記

相關文章