题目大意:给出一个字符串,将这个字符串的每个字符进行排列组合后,按字典序,输出第N个回文字符串
解题思路:要输出第N个字符串,首先要先判断是否能组成回文字符串,判断能否组成回文字符串的条件是,字符串中的每个字符的数量为奇数个的不能大于1。
然后进行排列组合,因为是回文字符串,所以两边是对称的,所以只需将左边的字符串求出来,然后按照对称性,就能得到右边的,
要求第N个回文字符串,我们先确定第一个字符(按字典序),然后求出在确定了一个字符的情况下有多少种的回文排列组合,如果该数量小于N,那么就用N-这种情况下的能组成回文数量,再换下一个字符来当第一个字符。
如果所有的情况都枚举完了,数量还小于N的话,那么就表示这个字符串的回文字符串数量<N,输出XXX
如果数量大于N的话,就表示这个字符能填在这个位置,然后再依次递归去求下一个位置的字符,直到能填充的字符数量为0,注意字符串中有一个字符数量为奇数的情况,这个数量为奇数的字符一定是放在最中间的,这样才能构成一个回文字符串
这里有一种特殊情况,是1个字符的情况,单个字符构成回文字符串,所以N>1的话,就会输出XXX
#include<cstdio>
#include<cstring>
char str[50];
long long N;
int sign, c[30];
long long A(int num) {
long long ans = 1;
for(int i = 2; i <= num; i++)
ans *= i;
return ans;
}
long long COUNT(int num) {
long long ans = 1;
while(num) {
ans *= num;
num--;
}
for(int i = 1; i <= 26; i++)
if(c[i])
ans /= A(c[i]);
return ans;
}
void dfs(int num, long long move) {
if(num == 0) {
if(move > 1)
printf("XXX");
else if(sign)
printf("%c",sign + 'a' - 1);
return ;
}
for(int i = 1; i <= 26; i++) {
if(c[i]) {
c[i]--;
long long ans = COUNT( num - 1);
if(ans < move)
move -= ans;
else {
printf("%c", i + 'a' - 1);
dfs(num-1,move);
printf("%c", i + 'a' - 1);
return ;
}
c[i]++;
}
}
printf("XXX");
}
void judge() {
memset(c,0,sizeof(c));
int dif, cnt;
for(int i = 0; str[i]; i++)
c[str[i]-'a'+1]++;
dif = cnt = sign = 0;
for(int i = 1; i <= 26; i++)
if(c[i] % 2) {
sign = i;
dif++;
}
if(dif > 1) {
printf("XXX");
return ;
}
for(int i = 1; i <= 26; i++) {
c[i] /= 2;
cnt += c[i];
}
dfs(cnt,N);
}
int main() {
int test,mark = 1;
scanf("%d",&test);
while(test--) {
printf("Case %d: ",mark++);
scanf("%s%lld",str,&N);
judge();
printf("\n");
}
return 0;
}