https://vjudge.net/contest/593228#problem/I
场上想的思路是 d p [ s ] [ i ] dp[s][i] dp[s][i] 现在还有 s s s 的没填,从 i i i 位置开始,最后的串,通过记忆化搜索来减少状态,但是还是过不了。
我们考虑继续扩展dp状态存的东西。 d p [ s ] dp[s] dp[s] 表示 s s s 已填,串的字典序最大和endposs的最大位置。然后 s s s 的补集必须在后面全部出现,因此我们就有一个右边界限制。假设我们枚举最后一个字符是 c c c,左边界就是 d p [ s − c ] . e n d p o s s dp[s-c].endposs dp[s−c].endposs
#include
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'||
ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//srand(time(0));
#define N 1010
#define M 22
int n, m, i, j, k, T;
struct PPPP {
int a, b, c;
};
struct node {
int pos, v[M];
void mem() { memset(v, 0, sizeof(v)); pos=0; }
node operator +(const PPPP &p) const {
node A; A=(*this);
A.v[p.a]+=p.b; A.pos=p.c;
return A;
}
void print() { for(int i=0; i<m; ++i) debug("%d ", v[i]); debug("\n"); }
}dp[1<<M];
unordered_map<int, node>mp;
int g[M][N], pre[N][M], nxt[N][M];
int f[1<<M], s, t, tot;
char str[N];
node max(node A, node B) {
for(int k=m; k>=1; --k)
if(A.v[k]>B.v[k]) return A;
else if(B.v[k]>A.v[k]) return B;
if(A.pos<B.pos) return A;
return B;
}
signed main()
{
#ifdef LOCALd
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
// T=read();
// while(T--) {
//
// }
int c[M];
memset(c, 0, sizeof(c));
n=read(); scanf("%s", str+1);
for(i=1; i<=n; ++i) str[i]-='a';
for(i=1, k=0; i<=n; ++i) if(!c[str[i]]) c[str[i]]=++k; //debug("c[%c]=%d\n", str[i]+'a', k);
m=k; //|S|
for(i=1; i<=n; ++i) str[i]=c[str[i]]-1;
for(i=1; i<=n; ++i) g[str[i]][i]++;
for(k=0; k<m; ++k) partial_sum(g[k]+1, g[k]+n+1, g[k]+1);
memset(c, 0, sizeof(c));
for(i=n; i>=0; --i) {
c[str[i]]=i;
memcpy(nxt[i], c, sizeof(c));
}
memset(c, 0, sizeof(c));
for(i=1; i<=n+1; ++i) {
memcpy(pre[i], c, sizeof(c));
c[str[i]]=i;
}
f[0]=n+1;
for(s=0; s<(1<<m); ++s) {
for(k=0; k<m; ++k) if((s>>k)&1) {
t=s-(1<<k);
if(!pre[f[t]][k]) continue;
f[s]=max(f[s], pre[f[t]][k]);
}
// debug("f[%d] = %d\n", s, f[s]);
}
dp[0].mem();
for(s=1; s<(1<<m); ++s) {
int cnt=__builtin_popcount(s);
dp[s].mem();
for(k=0; k<m; ++k) if((s>>k)&1) {
int t=s-(1<<k), cs=(1<<m)-1-s;
int l=dp[t].pos+1, r=f[cs]-1;
// debug("%d %d : [%d %d]\n", s, k, l, r);
if(r<l) continue;
int count=g[k][r]-g[k][l-1], lst=pre[r+1][k];
if(!cnt) continue;
dp[s]=max(dp[s], dp[t]+(PPPP){m-cnt+1, count, lst});
}
// debug("dp[%d].pos = %d\n", s, dp[s].pos);
}
auto t=dp[(1<<m)-1];
for(i=m; i>=1; --i) {
for(j=1; j<=t.v[i]; ++j) printf("%c", 'a'+i-1);
}
return 0;
}