https://codeforces.com/contest/1506/problem/G
題名:
小文字のみを含む長さnの文字列が与えられた場合、繰り返されるすべての文字(各文字に1つ)を削除し、残りの文字列を辞書式に最大にして、残りの文字列を出力します。n≤2×10 ^ 5
アイデア:
暴力を振るう可能性がある場合は、まず暴力について考えてください。
答えの長さは最大で26であることがわかりました。したがって、最初にさまざまな種類の文字の数を数え、答えの長さはそのサイズです。
次に、答えの各ビットについて、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
この問題は、単調なスタック最適化を使用することです。
数字を削除するのはコツのようです
https://codeforces.ml/gym/102890/problem/M
更新予定