解题思路:
要想使结果的字典序最小,就应该尽可能地将小的元素留在前面。
1. 如果stk栈顶的元素大于当前遍历到的元素,根据上述原则,则应该在条件允许的情况下回避这一情况,根据题目要求(去重),只有在后续还有这个栈顶元素的情况下才能将这个栈顶元素去掉(减少一个逆序)。如果后续没有这个栈顶元素,则只能将它保留在这儿,即使它大于它的下一个元素。
2. 这样一来,stk中保存的两两相邻元素要么是顺序的(前一个小于后一个)要么是逆序的(前一个大于后一个,虽有违背最小排列,但由于没有后续相同元素可以替换,只能这样将就)。
3. 由2)可知如果当前元素存在于stk中,则说明stk中的这个元素已经是顺序的了(^-^),所以应该在循环中先判断这一情况,直接跳过当前元素。
4. 同时,当根据条件去掉一个栈顶元素时,新的栈顶元素可能也大于当前元素,所以应该继续判断新的栈顶元素和当前元素的关系,用一个while循环,直到出现一个不满足循环条件的栈顶元素(要么小于当前元素,要么大于当前元素但后续又无与之相同的元素来替代)。
作者:fu-guang
链接:https://leetcode-cn.com/problems/remove-duplicate-letters/solution/c-0ms-ji-bai-100jian-ming-dai-ma-chao-xiang-xi-jie/
来源:力扣(LeetCode)
补充:
这里可以使用 string 来 代替 stack ,最后的结果可以直接返回。
class Solution {
public:
string removeDuplicateLetters(string s)
{
vector<int> vis(26 , 0), num(26 , 0);//num用于统计次数 vis用于判断是否使用过
for (char ch : s)
{
num[ch - 'a']++;
}
int retsize = 0; //实际应该返回的字符个数
for(int i = 0 ; i <26 ;++i)
{
if(num[i])
++retsize;
}
stack<char> st; //利用栈模拟
st.push('A');
for(char ch : s)
{
if(vis[ch - 'a'] == 0)//这个字符没有用过
{
while(st.top() > ch) //这个字符大小 < 栈顶元素,为了保证字典序需要弹出栈顶
{
if(num[st.top() - 'a'] > 0 ) //回退,弹出栈顶元素
{
vis[st.top() - 'a'] = 0;//将弹出的元素设置为未使用
st.pop();
}
else //只有一个字符,直接入栈,即使这个元素 无法满足升序要求
{
break;
}
}
vis[ch - 'a'] = 1;//标记为已使用
st.push(ch);
}
num[ch - 'a']--; //可用次数--
}
string ret;
ret.resize(retsize);
for(int i = retsize-1 ; i >= 0 ;--i)
{
ret[i] = st.top();
st.pop();
}
return ret;
// 2.
// string stk;
// for (char ch : s)
// {
// if (!vis[ch - 'a'])
// {
// while (!stk.empty() && stk.back() > ch)
// {
// if (num[stk.back() - 'a'] > 0)
// {
// vis[stk.back() - 'a'] = 0;
// stk.pop_back();
// } else
// {
// break;
// }
// }
// vis[ch - 'a'] = 1;
// stk.push_back(ch);
// }
// num[ch - 'a'] -= 1;
// }
// return stk;
}
};