uva 11027 - Palindromic Permutation(组合数)

题目链接: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;
}


你可能感兴趣的:(uva 11027 - Palindromic Permutation(组合数))