题意:
给出字符串,以及n,然后字符串中的字母排序可以组成若干的字符串,有些为回文串,输出第n个回文串,若不存在第n个回文串,输出“XXX”。
解析:
因为n非常大,所以用枚举是由点不太现实的,对于一个字符串,若能重排成回文串,说明每个字母出现的次数都为偶数,或者说为奇数的只有一个(可以放在中间);
然后这样我们就可以将字符缩减一半,构造左半边的字符串(注意若有单个字符输出时要加上)。
然后根据左边的回文串,构造出目标回文串。
至于怎么求出第K大的全排列字符串,我觉得这篇博文讲的很详细。
http://www.cnblogs.com/scau20110726/archive/2013/02/04/2891543.html
AC代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 50;
char str[N], ans[N], mid;
ll factor[N], cnt[30];
int n;
int tot, ok;
void get_factor() { //计算阶乘
factor[0] = 1;
for(int i = 1; i < 16; i++) {
factor[i] = factor[i-1] * i;
}
}
ll fun(int sum) { //传入该可重集的个数,返回该可重集的全排列的个数
ll ret = 1;
for(int i = 0; i < 26; i++) {
ret *= factor[cnt[i]];
}
return factor[sum] / ret;
}
void init() {
memset(ans, 0, sizeof(ans));
memset(cnt, 0, sizeof(cnt));
mid = 0;
}
void output(int len) {
for(int i = 0; i < len; i++)
putchar(ans[i]);
if(mid) putchar(mid);
for(int i = len - 1; i >= 0; i--)
putchar(ans[i]);
putchar('\n');
}
bool dfs(int cur) {
if(cur == tot) {
return true;
}
for(int i = 0; i < 26; i++) {
if(cnt[i]) {
cnt[i]--;
ll ret = fun(tot - cur - 1);
cnt[i]++;
if(ret < n) {
n -= ret;
}else {
ans[cur++] = 'a' + i;
cnt[i]--;
if(dfs(cur)) {
return true;
}
}
}
}
return false;
}
int main() {
get_factor();
int T, cas = 1;
scanf("%d", &T);
while(T--) {
init();
scanf("%s%d",str, &n);
for(int i = 0; str[i]; i++) { //统计每个字符出现的次数
cnt[str[i] - 'a']++;
}
int flag = 0, sum = 0;
for(int i = 0; i < 26; i++) {
if(cnt[i] & 1) {
flag++;
mid = 'a' + i;
}
cnt[i] >>= 1;
sum += cnt[i];
}
printf("Case %d: ", cas++);
if(flag > 1 || fun(sum) < n) {
printf("XXX\n");
}else {
tot = sum, ok = false;
if(dfs(0))
output(sum);
}
}
return 0;
}