给我们一个长度不超过 20 个字母的字符串 s,为第 k 小的字符串是哪个? 如果不存在输出 - 1
思路
这题的本质就是 逆康托展开,但是由于 s 中可能出现重复的字符,因此我们需要在 逆康托展开 的基础上稍微变一下
求解字符串的时候有一个重要的贪心思想:如果第 d 位置选择 i 字母的方案数 大于等于 k 的话, 第 i 个字母一定是 d 位置所需要的字符!!!
代码
#include<bits/stdc++.h>
using namespace std;#definedbdouble#definelllonglong#definescscanf#defineprprintf#definefifirst#definesesecond#definepbpush_back#definem_pmake_pair#definePirpair<int,int>#defineinf0x3f3f3f3f#defineINF0x3f3f3f3f3f3f3f3f/*==========ACMer===========*/constint mxn =30;int N, K;
ll C[mxn][mxn], cnt[mxn];char s[mxn];voidinit(){
C[0][0]=1;for(int i =1; i <=20; i ++){
C[i][0]=1;for(int j =1; j <= i; j ++)
C[i][j]= C[i -1][j]+ C[i -1][j -1];}}
ll count(int n){
ll res =1;for(int i =0; i <26; i ++){
res *= C[n][cnt[i]];
n -= cnt[i];}return res;}voidsov(int d,int k){if(d > N){pr("\n");return;}for(int i =0; i <26; i ++){if(cnt[i]==0)continue;
cnt[i]--;
ll num =count(N - d);if(num >= k)//贪心思想选字母:如果第 d 位置选择 i 字母的方案数 大于等于 k 的话, 第 i 个字母一定是 d 位置所需要的字符{pr("%c", i +'a');break;}else
k -= num;//这里要减去是因为: 从当前的 i 递增下去产生的方案数都是不行的
cnt[i]++;}sov(d +1, k);}intmain(){init();int T, cas =1;sc("%d",&T);while(T --){sc("%s %d", s,&K);
N =strlen(s);memset(cnt,0,sizeof cnt);for(int i =0; i < N; i ++)
cnt[s[i]-'a']++;pr("Case %d: ", cas ++);if(count(N)< K)pr("Impossible\n");elsesov(1, K);}return0;}