UVA 11027 Palindromic Permutation(组合数学)

题意:
给出字符串,以及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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值