1 题目
题目:移除多余字符(Remove Duplicate Letters)
描述:给定一个字符串s由小写字符组成,移除多余的字符使得每个字符只出现一次。你必须保证结果是字典序是最小的合法字符串。
lintcode题号——843,难度——medium
样例1:
输入: s = "bcabc"
输出: "abc"
样例2:
输入: s = "cbacdcbc"
输出: "acdb"
2 解决方案
2.1 思路
去除重复可以使用set数据结构来做,但是题中还要求去重之后的字符串结果是字典序最小的,可以考虑使用栈来对每个元素进行细节判断,在压入某个元素的时候,判断之前压入栈中的元素是否符合要求,如果不符合要求,则弹出不符合要求的元素后,再进行压入。入栈的判断条件为当前元素大于栈顶元素,或者栈顶元素在之后不会再出现了。
2.2 时间复杂度
时间复杂度为O(n)。
2.3 空间复杂度
空间复杂度为O(1)。
3 源码
细节:
- 需要使用map先保存每个元素的出现次数,用于判断该元素在当前位置之后是否还存在。
- 模拟栈的操作过程,可以直接使用string,不用新建一个stack。
- 对比当前元素与栈顶元素大小,若当前元素比栈顶小,且栈顶元素之后还会出现,则弹出栈顶,压入当前元素。(注意依次弹出所有不达标的栈顶要使用while)
- 使用set来跳过重复的元素。
C++版本:
/**
* @param s: a string
* @return: return a string
*/
string removeDuplicateLetters(string &s) {
// write your code here
string result = "";
// 计算每个字符出现的次数
unordered_map<char, int> letterCount;
for (auto c : s)
{
if (letterCount.find(c) != letterCount.end())
{
letterCount.at(c)++;
}
else
{
letterCount.insert({c, 1});
}
}
unordered_set<char> letterSet; // 使用集合跳过重复
for (auto c : s)
{
letterCount.at(c)--;
// 跳过重复的字符
if (letterSet.find(c) != letterSet.end())
{
continue;
}
// 模拟栈,用于进行字典序排列,判断当前入栈元素是否小于栈顶元素,且栈顶元素之后还会出现
while (!result.empty() && result.back() > c && letterCount.at(result.back()) > 0)
{
// 依次弹出栈顶元素
letterSet.erase(result.back());
result.pop_back();
}
letterSet.insert(c);
result.push_back(c);
}
return result;
}