思路:
这个题目描述的就很迷,是看了讨论才明白在问什么。
题目说是包含所有不同字符的最小子序列, 所有不同字符说明 s字符串中所有出现过的字符都要有,最小说明满足前一个条件后,子序列要尽量让小字符排在前面,如ab和ba,那ab就是更小的。子序列,说明要是例子里面的子序列,如"cbacdcbc"
,输出为acdb,而不是abcd是因为abcd并不是子序列,acd才是子序列。
那么我们要确定如何保证获得最小的子序列呢,这里需要用栈,因为有滞后处理,我们需要st.top()和s[i]比较 并且满足一定条件后能保存在栈里。
首先我们要记录 每个字符出现的次数,为什么呢?因为我们要比较st.top()是否大于s[i],如果大那么应该要pop掉并把s[i]加入栈顶,那着有个问题,万一pop掉的字符是最后一个字符呢,那样就不满足所有不同字符这个条件了。 所以我们需要记录字符的个数来确定不会把这个字符最后一个给pop走。
如果st.top()小于S[i],那么就应该加入,但如果后面还遇到了相同的字符,那么我们就应该还需要一个办法来判断栈里面是否有这个元素,因为原st.top都小于S[i]了,如果在遇到原st.top的字符,那么原s[i]做top后,是不是st.top是不是大于现在的s[i],这样就要去掉top加入这个字符,那么就有两个重复字符在栈里面了,并且还去除了正确的字符。
所以整体的思路就该是,先用一个数组记录每个字符的数量,通过s[i]-‘a’来获得数字来存放在数组下标中。然后再次遍历s,判断遍历到的字符ch有没有加入过栈,如果有就把这个字符的数量减一,否则就循环判断栈是否为空并且st.top是否大于ch,如果满足就判断栈顶元素是不是这个字符的最后一个,如果不是就pop掉,是就结束循环。如果循环条件为st.top大于ch或者栈为空的循环结束后,就加入ch进栈。最后再把栈里的元素取出来,因为是先加的小字符,所以最后还要倒序
代码:
class Solution {
public:
string smallestSubsequence(string s) {
vector<int> num(26),exist(26);
stack<char> st;
for(char ch:s){
num[ch-'a']++; //记录相同字符的数量
}
for(char ch:s){
if(!exist[ch-'a']){ //如果ch存在就不需要在处理了,
while(!st.empty()&&st.top()>ch){ //栈顶元素是否大于ch?这一步是为了选最小序列
if(num[st.top()-'a']>0){ //如果栈顶元素是这个字符最后一个就不能pop掉
exist[st.top()-'a']=0;
st.pop();
}
else break;
}
exist[ch-'a']=1; //不管栈顶元素大于ch还是小于,都需要把ch加进来,只是原栈顶元素还在不在而已
st.push(ch);
}
num[ch-'a']--; //ch加入后 后面的相同字符的数量
}
string word="";
while(!st.empty()){
word+=st.top();
st.pop();
}
reverse(word.begin(),word.end());
return word;
}
};