LeetCode #316 Remove-Duplicate Letters 数组 字符串

Description  


Given a string which contains only lowercase letters, remove duplicate letters so that every letter appears 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"

思路


老年人回炉重造第4天。

这道题挺有难度的,自己想,没想出来,看了油管的一个讲解视频有了思路后才敲出来,视频链接如下:

LeetCode 316. Remove Duplicate Letters 中文解释 Chinese Version

本题的难点之一是如何正确理解题目,我的想法是这样:

输入:一个仅含小写字母的字符串

输出:字典序最小的字符串,每个字母只出现一次,但是如果有一个字母在输入串中只出现一次,那么它在输出串的位置不能变化,而出现多次的元素的位置是可以相互替换的,

比如输入串为"b e c a b d c", b出现了两次,所以在输出串中,b可以是首字母,也可以出现在"e, c, a"这三个字母之后;但是,因为输入串只有一个 e 且 e 在第二个位置,那么无论怎么排序,输出串必须是一个首字母或第二个字母字母为 e 的的字符串。

理解完题目后,开始解题。

遍历输入串,使用一个 map 统计各字母的出现次数,时间复杂度为 O(n)。

之后再一次遍历输入串,并使用一个 string 对象 result 存储最终的结果,当前扫描的字符需要和 result 最后的字符进行循环比较,如果当前扫描字符的字典序更小,且 result 最后的字符在后续扫描中还会出现(说明位置可以被替换,如上面例子中的 b),就把 result 尾字符 pop 出去。

同时,还需要加入一个访问数组,标记某个字符是否已被访问,防止同个字符重复加入 result 的 BUG。

 

值得注意的是,C++11之前,string 并没有很好的函数以常数的时间删除最后一个字符,C++11之后才出现了常数时间的 pop_back()。

令人奇怪的是,vector<char> 的增删操作有时候好像比 string 的效率要高不少的样子,能达到4ms, 但是 string 稳定8ms,可是 string 底层是 vector<char> 实现的,这就有点玄学了,之后再认真研究一波。

AC后,发现 unordered_map 和 map 的效率居然相差很多(ranking 40% 和 10% 的区别),有些吃惊,后来查阅资料发现,两个容器的应用场景如下:

map 基于红黑树,能稳定保持查询效率为O(logn)
unordered_map 基于Hash,最好查询效率是O(1)

10w、100w量级的耗时,可以发现,map在增删查三项上均弱于unordered_map,而内存使用的话map略少,但不明显

⭐️结论:在需要(关键字)有序性或者对单次查询有时间要求的应用场景下,应使用map,其余情况应使用unordered_map

AC代码如下:

vector版本

class Solution {
public:
    string removeDuplicateLetters(string s) {
        if (s.size() == 0) return s;
        
        // map from letter to numbers appeared in remaining string
        unordered_map<char, int> remain_cnt;
        // map from letter to whether the letter is used
        unordered_map<char, bool> is_used;
        
        for (auto letter : s) {
            auto it = remain_cnt.find(letter);
            if (it == remain_cnt.end()) {
                remain_cnt[letter] = 1;
                is_used[letter] = false;
            } else {
                it->second = it->second + 1;
            }
        }
        
        vector<char> result;
        for (auto letter : s) {
            auto remain_cnt_it = remain_cnt.find(letter);
            auto letter_used_it = is_used.find(letter);
            
            if (result.empty()) {
                result.push_back(letter);
                remain_cnt_it->second = remain_cnt_it->second - 1;
                letter_used_it->second = true;
                continue;
            }

            if (letter_used_it->second) {
                remain_cnt_it->second = remain_cnt_it->second - 1;
                continue;
            }
            
            auto last_letter = result.back();
            if (remain_cnt[last_letter] != 0 
                    && last_letter > letter) {
                while (remain_cnt[last_letter] != 0 &&
                        last_letter > letter) {
                    result.pop_back();
                    is_used[last_letter] = false;
                    if (!result.empty()) {
                        last_letter = result.back();
                    } else {
                        break;
                    }
                }
                result.push_back(letter);
                remain_cnt_it->second = remain_cnt_it->second - 1;
                is_used[letter] = true;
            } else {
                result.push_back(letter);
                remain_cnt_it->second = remain_cnt_it->second - 1;
                is_used[letter] = true;
            }
        }
        
        return string(result.cbegin(), result.cend());
    }
};

string 版本(其实和 vector 的接口是一样的,语法上并没有区别)

class Solution {
public:
    string removeDuplicateLetters(string s) {
        if (s.size() == 0) return s;
        
        // map from letter to numbers appeared in remaining string
        unordered_map<char, int> remain_cnt;
        // map from letter to whether the letter is used
        unordered_map<char, bool> is_used;
        
        for (auto letter : s) {
            auto it = remain_cnt.find(letter);
            if (it == remain_cnt.end()) {
                remain_cnt[letter] = 1;
                is_used[letter] = false;
            } else {
                it->second = it->second + 1;
            }
        }
        
        string result;
        for (auto letter : s) {
            auto remain_cnt_it = remain_cnt.find(letter);
            auto letter_used_it = is_used.find(letter);
            
            if (result.empty()) {
                result.push_back(letter);
                remain_cnt_it->second = remain_cnt_it->second - 1;
                letter_used_it->second = true;
                continue;
            }

            if (letter_used_it->second) {
                remain_cnt_it->second = remain_cnt_it->second - 1;
                continue;
            }
            
            auto last_letter = result.back();
            if (remain_cnt[last_letter] != 0 
                    && last_letter > letter) {
                while (remain_cnt[last_letter] != 0 &&
                        last_letter > letter) {
                    result.pop_back();
                    is_used[last_letter] = false;
                    if (!result.empty()) {
                        last_letter = result.back();
                    } else {
                        break;
                    }
                }
                result.push_back(letter);
                remain_cnt_it->second = remain_cnt_it->second - 1;
                is_used[letter] = true;
            } else {
                result.push_back(letter);
                remain_cnt_it->second = remain_cnt_it->second - 1;
                is_used[letter] = true;
            }
        }
        
        return result;
    }
};
原文地址:https://www.cnblogs.com/Bw98blogs/p/12633712.html