链接:https://ac.nowcoder.com/acm/contest/551/D
来源:牛客网
题目描述
CSL 以前不会字符串算法,经过一年的训练,他还是不会……于是他打算向你求助。
给定一个字符串,只含有可打印字符,通过删除若干字符得到新字符串,新字符串必须满足两个条件:
- 原字符串中出现的字符,新字符串也必须包含。
- 新字符串中所有的字符均不相同。
- 新字符串的字典序是满足上面两个条件的最小的字符串。
输入描述:
仅一行,有一个只含有可打印字符的字符串 s。
|s|≤10^5
输出描述:
在一行输出字典序最小的新字符串。
示例1
输入
bab
输出
ab
示例2
输入
baca
输出
bac
备注:
ASCII字符集包含 94 个可打印字符(0x21 - 0x7E),不包含空格。
题解
先预处理一下字符串,计算出每个字符出现的次数。
用数组ans模拟栈来存储选择的字符,cnt记录栈内字符个数,ans[cnt]是栈顶元素。
用cnt数组flag标记字符是不是在栈内
然后从左到右扫一遍,对于每个位置会出现两种情况。
1.这个字符已经在栈内,那么跳过,不用处理。
2.这个字符不在栈内,那么比较这个字符与栈顶元素的大小 ,如果栈顶字符大于当前字符,并且栈顶字符在之后的位置还会出现,那么出栈,然后继续比较。知道不满足上述情况,当前位置的元素进栈。
对于每个位置的字符,最多入栈一次,出栈一次,所以时间复杂度是O(n)
代码
#include<algorithm>
#include <iostream>
#include<cstring>
#include <cstdio>
#include<stack>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int num[200];
char c[maxn];
char ans[maxn];
int cnt=0;
bool flag[200];
int main(){
scanf("%s",c);
int len=strlen(c);
for(int i=0;i<len;i++) num[c[i]]++;
for(int i=0;i<len;i++){
num[c[i]]--;
if(flag[c[i]]) continue;
else{
while(cnt>0&&c[i]<ans[cnt]&&num[ans[cnt]]){
flag[ans[cnt]]=false;
cnt--;
}
cnt++;
ans[cnt]=c[i];
flag[c[i]]=true;
}
}
for(int i=1;i<=cnt;i++) printf("%c",ans[i]);
cout<<endl;
return 0;
}