问题描述
给你一个仅包含小写字母的字符串,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
示例 1:
输入: “bcabc”
输出: “abc”
示例 2:
输入: “cbacdcbc”
输出: “acdb”
输入说明
输入一个仅包含小写字母的字符串
cbacdcbc
输出说明
输出结果。首尾无多余空格或空行
acdb
代码思路
首先考虑到字典序需要由小到大且相对位置不能变,在遍历时应倒序删除,例如对例1:“bcabc”,b,c可以先保存下来,当遍历到a时,因为a的字典序比较小,这个时候要考虑后面的序列中是否还有b和c,如果有,则需要将已经保存的b,c删除,以保证最终字符串的字典序最小。在删除时要保证相对位置不变,所以考虑用栈来存放字符。
具体如下:
1、首先需要记录每个字符出现的次数(帮助判断字符是否在后面出现)
2、遍历字符串,如果栈为空,则直接将当前字符入栈;
如果栈不为空,则需要判断当前字符和栈顶元素的字典序大小,若栈顶元素的字典序小于当前元素,且栈顶元素在当前字符后面不再出现,当前字符入栈;若栈顶元素大于当前字符且在当前字符后仍然出现,则将栈顶元素出栈,继续判断新的栈顶元素。
#include<iostream>
#include<string>
#include<vector>
#include<stack>
using namespace std;
class Solution {
public:
string Remove(string s){
vector<int> vis(26), num(26);
//用num来记录每个字符出现的次数,vis记录字符是否出现在栈里(即是否出现在已访问的字符中)
for (char ch : s) {
num[ch - 'a']++;
}
string stk;
for (char ch : s) {
if (!vis[ch - 'a']) {
//当前字符没有在栈中出现过
//如果栈顶元素比当前字符大,且栈顶元素还存在于后面未访问过的字符中,栈顶元素出栈,当前元素进栈
while (!stk.empty() && stk.back() > ch &&num[stk.back() - 'a']) {
vis[stk.back() - 'a'] = 0;
stk.pop_back();
}
vis[ch - 'a'] = 1;
stk.push_back(ch);
}
//当前字符的出现次数减一
num[ch - 'a']--;
}
return stk;
}
};
int main()
{
string s1,s2;
cin>>s1;
s2=Solution().Remove(s1);
cout<<s2;
return 0;
}