https://codeforces.com/contest/1506/problem/G
题意:
给出一个长度为 n的只包含小写字母的字符串要删掉其中的所有重复字符(每个字符必须留一个),使留下来的字符串字典序最大,输出留下的字符串。 n≤2×10^5
思路:
能暴力就先思考暴力。
我们发现答案最多是长度为26的。于是先统计不同种类字母个数,答案长度就是其size。
然后对于答案的每一位,我们可以O(26)枚举每一个字符。然后考虑,当前字符如果选了,后面的不同字符的种类个数够不够。够的话就选。不够的话就继续枚举。
于是快速判断后面不同字符的种类够不够,后缀预处理。
O(26*26*n)==1e8 (2.5s)够
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=2e5+1000;
typedef int LL;
inline LL read(){LL x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
char str[maxn];
LL sum[maxn];///后缀不同种类数字的个数
bool st[maxn][30];///后缀每个种类数字的存在状态
bool del[maxn];
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
LL t;cin>>t;
while(t--){
cin>>(str+1);
LL n=strlen(str+1);
for(LL i=0;i<n+10;i++) sum[i]=0,del[i]=0;
for(LL i=0;i<n+10;i++){
for(LL j=0;j<=29;j++) st[i][j]=0;
}
for(LL i=n;i>=1;i--){
sum[i]=sum[i+1];
for(LL j=1;j<=26;j++){
st[i][j]=st[i+1][j];
}
if(st[i][str[i]-'a'+1]==0){
st[i][str[i]-'a'+1]=1;
sum[i]++;
}
}
LL m=sum[1];
vector<char>v;
LL op=1;
for(LL l=1;l<=m;l++){
for(char c='z';c>='a';c--){
if(st[op][c-'a'+1]==false) continue;
bool flag=0;
for(LL i=op;i<=n;i++){
if(flag){
if(st[i][c-'a'+1]==1){
st[i][c-'a'+1]=0;
sum[i]--;
}
if(str[i]==c) del[i]=1;
}
if(del[i]) continue;
if(str[i]==c){
LL temp=sum[i+1];///后面的有效种类个数
if(st[i+1][c-'a'+1]) temp--;///如果涵盖当前字母,--;
if(temp>=m-l){
flag=1;
op=i+1;
}
}
}
if(flag){
v.push_back(c);
break;
}
}
}
for(auto i:v){
cout<<i;
}
cout<<"\n";
}
return 0;
}
后来群里说是道原题。
https://ac.nowcoder.com/acm/contest/12606/E
这道题就是要用单调栈优化的。
好像还是个删数问题的trick
https://codeforces.ml/gym/102890/problem/M
待update