题目链接:uva 11027 - Palindromic Permutation
题目大意:给出字符串,以及n,然后字符串中的字母排序可以组成若干的字符串,有些为回文串,输出第n个回文串,若不存在第n个回文串,输出“XXX”。
解题思路:以为n非常大,所以用枚举是由点不太现实的,对于一个字符串,若能重排成回文串,说明每个字母出现的次数都为偶数,或者说为奇数的只有一个(可以放在中间);然后这样我们就可以将字符缩减一半,构造左半边的字符串(注意若有单个字符输出时要加上)。
接下来就是枚举各个位置上的字符了,例如aaaabb, 有4个a, 2个b(处理完的,即原先有8个a,4个b),n为11.
若‘a'放在第一位,还剩3个a和2个b,可以组成(3 + 2)! / (3!*2!) = 10. 因为10 < 11,所以说第一位不能是’a', n -= 10, 然后n = 1.
现在考虑‘b'放在第一位,还剩4个a和1个b, 可以组成(4 + 1)! / (4!*1!)= 5, 因为5 > 1,所以说可以确定第一位是’b',这是n不用减。
然后一次类推,构造出目标回文串。
#include <stdio.h>
#include <string.h>
const int N = 50;
long long tmp[N];
long long n, c[N];
void init() {
tmp[0] = 1;
for (long long i = 1; i <= 15; i++)
tmp[i] = tmp[i - 1] * i;
}
long long count(int t) {
long long sum = 1;
for (int i = 0; i < 26; i++)
sum *= tmp[c[i]];
return tmp[t] / sum;
}
void solve() {
int cnt = 0, sum = 0, a = 0;
char ans[N], ch = '\0';
for (int i = 0; i < 26; i++) {
if (c[i] % 2) {
ch = 'a' + i;
cnt++;
}
sum += c[i] /= 2;
}
if (cnt > 1 || count(sum) < n) {
printf("XXX\n");
return ;
}
bool flag = false;
while (sum != a) {
for (int i = 0; i < 26; i++) {
if (c[i]) {
c[i]--;
long long k = count(sum - a - 1);
c[i]++;
if (n <= k) {
ans[a++] = 'a' + i;
c[i]--;
break;
} else {
n -= k;
}
}
}
}
for (int i = 0; i < a; i++)
printf("%c", ans[i]);
if (ch != '\0') printf("%c", ch);
for (int i = a - 1; i >= 0; i--)
printf("%c", ans[i]);
printf("\n");
}
int main () {
init();
int cas, t = 1;
char str[N];
scanf("%d", &cas);
while (cas--) {
scanf("%s %lld", str, &n);
int len = strlen(str);
memset(c, 0, sizeof(c));
for (int i = 0; i < len; i++)
c[str[i] - 'a']++;
printf("Case %d: ", t++);
solve();
}
return 0;
}