当前位置: 首页 > news >正文

UVa 11027 Palindromic Permutation

题目描述

给定一个由小写字母组成的字符串,我们可以对其字符进行排列,生成新的字符串,并将这些字符串按字典序排序。在这些排列中,我们需要找出第 nnn 个回文排列(按字典序排序后的回文串列表中的第 nnn 个)。如果回文排列的总数小于 nnn,则输出 "XXX"

示例

输入:

3
abba 1
abba 2
abba 3

输出:

Case 1: abba
Case 2: baab
Case 3: XXX

题目分析

1. 回文排列的性质

一个字符串能够排列成回文串,必须满足以下条件之一:

  • 如果字符串长度为偶数,那么每个字符的出现次数必须都是偶数。
  • 如果字符串长度为奇数,那么恰好有一个字符的出现次数为奇数,其余字符的出现次数均为偶数。

如果上述条件不满足,则无法形成任何回文排列,直接输出 "XXX"

2. 回文排列的构造方法

对于能够形成回文排列的字符串,我们可以将其分成两半:

  • 左半部分:由每个字符取 出现次数2\frac{\text{出现次数}}{2}2出现次数 个组成。
  • 中间字符(仅当字符串长度为奇数时):即出现次数为奇数的那个字符。
  • 右半部分:左半部分的镜像反转。

因此,问题转化为:

  1. 检查字符串是否能形成回文。
  2. 构造左半部分的字符集合。
  3. 计算左半部分的不同排列数。
  4. 找到左半部分的第 nnn 个排列(按字典序)。
  5. 组合成完整的回文串。

3. 排列计数与生成

由于字符串长度最多为 303030,左半部分长度最多为 151515。虽然 15!15!15! 的数值很大,但由于字符可能重复,实际排列数会少很多。

我们可以使用递归 + 计数的方法,按位置依次确定字符,并计算以某前缀开头的排列数,从而直接生成第 nnn 个排列,而不需要枚举所有排列。

排列计数公式(含重复字符)

对于字符计数 cnt0,cnt1,…,cnt25cnt_0, cnt_1, \dots, cnt_{25}cnt0,cnt1,,cnt25,左半部分的排列数为:

total=(∑cnti)!∏(cnti!) \text{total} = \frac{(\sum cnt_i)!}{\prod (cnt_i!)} total=(cnti!)(cnti)!

在递归生成第 kkk 个排列时,我们按字典序尝试每个可能的字符,计算如果当前位放置该字符,后面有多少种排列。如果 kkk 大于这个数,就跳过并减去;否则选定该字符,并递归到下一层。


解题思路

步骤概述

  1. 统计字符频率:记录每个字符的出现次数。
  2. 检查回文可行性
    • 如果长度为偶数,所有字符出现次数必须为偶数。
    • 如果长度为奇数,恰好有一个字符出现次数为奇数。
  3. 构造左半部分:每个字符取 出现次数2\frac{\text{出现次数}}{2}2出现次数 个。
  4. 计算回文总数:即左半部分的不同排列数。
  5. 生成第 nnn 个回文
    • 如果 n>totaln > \text{total}n>total,输出 "XXX"
    • 否则,生成左半部分的第 nnn 个排列,并组合成完整回文。

参考代码

// Palindromic Permutation
// UVa ID: 11027
// Verdict: Accepted
// Submission Date: 2025-11-04
// UVa Run Time: 0.000s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net#include <bits/stdc++.h>
using namespace std;typedef long long ll;// 计算阶乘
ll factorial[20];void init_factorial() {factorial[0] = 1;for (int i = 1; i <= 15; i++) {factorial[i] = factorial[i - 1] * i;}
}// 计算给定字符频率下的排列数
ll count_permutations(const vector<int>& counts) {int total = 0;for (int c : counts) total += c;ll result = factorial[total];for (int c : counts) {result /= factorial[c];}return result;
}// 递归生成左半部分的第 k 个排列
string build_kth(vector<int>& counts, int len, ll k) {if (len == 0) return "";int total_chars = 0;for (int i = 0; i < 26; i++) total_chars += counts[i];for (int i = 0; i < 26; i++) {if (counts[i] == 0) continue;// 尝试将字符 'a' + i 放在当前位置counts[i]--;ll ways = count_permutations(counts);if (k <= ways) {return string(1, 'a' + i) + build_kth(counts, len - 1, k);} else {k -= ways;counts[i]++;}}return ""; // 不会执行到这里
}void solve(int case_num) {string s;ll n;cin >> s >> n;// 统计频率vector<int> freq(26, 0);for (char c : s) {freq[c - 'a']++;}// 检查能否形成回文int odd_count = 0;char odd_char = 0;for (int i = 0; i < 26; i++) {if (freq[i] % 2 == 1) {odd_count++;odd_char = 'a' + i;}}int len = s.length();if ((len % 2 == 0 && odd_count != 0) || (len % 2 == 1 && odd_count != 1)) {cout << "Case " << case_num << ": XXX" << endl;return;}// 构造左半部分的字符计数vector<int> half_counts(26, 0);for (int i = 0; i < 26; i++) {half_counts[i] = freq[i] / 2;}int half_len = len / 2;// 计算左半部分的不同排列数ll total_palindromes = count_permutations(half_counts);if (n > total_palindromes) {cout << "Case " << case_num << ": XXX" << endl;return;}// 生成左半部分的第 n 个排列string left_half = build_kth(half_counts, half_len, n);// 构造完整回文string right_half = left_half;reverse(right_half.begin(), right_half.end());string palindrome;if (len % 2 == 0) {palindrome = left_half + right_half;} else {palindrome = left_half + odd_char + right_half;}cout << "Case " << case_num << ": " << palindrome << endl;
}int main() {init_factorial();int T;cin >> T;for (int t = 1; t <= T; t++) {solve(t);}return 0;
}
http://www.dtcms.com/a/568862.html

相关文章:

  • Python模板注入漏洞
  • 【SMTP】在线配置测试工具,如何配置接口?
  • 黑马JAVAWeb-01 Maven依赖管理-生命周期-单元测试
  • 第12讲:入门级状态管理方案 - Provider详解
  • 单调栈的“视线”魔法:统计「队列中可以看到的人数」
  • 【2025 SWPU-NSSCTF 秋季训练赛】WebFTP
  • 海淀教育互动平台网站建设哪些网站是wordpress
  • 网站开发定制宣传图片北京百度推广排名优化
  • ELK企业级日志分析系统学习
  • 360开源FG-CLIP2,给人工智能升级了精准的视觉解析系统
  • 关于dify中http节点下载文件时,文件名不为原始文件名问题解决
  • 期中考试成绩查询系统制作方法
  • Vue 用户管理系统(路由相关练习)
  • AI时代的新SEO玩法:使用SERP API构建排名追踪系统
  • 宝塔配置:IP文件配置,根据端口配置多个项目文件(不配置域名的情况)
  • 网站布局怎么设计哪个网站可以学做蛋糕
  • 深圳网站设计兴田德润信任高网站推广目标是什么
  • Java并发List实战:CopyOnWriteArrayList原理与ArrayList常见面试题
  • 【JavaEE】-- Cookie Session
  • --- Http和Https协议 ---
  • 无HTTP服务时的文件传输方法大全
  • 微信注册重庆网站优化方式
  • Spring Boot Web上下文工具类详解:获取Request、Response和参数
  • php购物网站开发实例源码杭州小程序制作公司排行榜
  • 测试开发话题09---自动化测试常用函数(1)
  • Linux - Fail2ban搭配 firewallcmd-rich-rules 完整部署教程
  • 使用 nvm 安装 Node.js
  • Arbess零基础学习 - 使用Arbess+GitLab实现Node.js项目自动化构建/主机部署
  • Linux 虚拟化技术 KVM/ESXI/Docker
  • C006基于博途西门子1200PLC机械手分拣物料控制系统仿真