回文
对一个包含 $2n$ 个整数(数字 $1 \sim n$ 各出现两次)的序列 $a$ 进行 $2n$ 次弹出操作:每次只能从序列 $a$ 的开头(记为 L)或末尾(记为 R)取出一个数依次加入新序列 $b$,使得最终生成的序列 $b$ 构成回文串。如果存在多种可行的操作方案,需输出字典序最小的操作序列(即包含字符 L 和 R 的字符串);若无法构成回文串则输出 -1。
CSP-S1分钟阅读

本题中“字典序最小的操作方案”指的是:由 个操作字符('L' 与 'R')拼成的字符串字典序最小。
由于字符的 ASCII 码关系为 'L' 'R',比较两个方案时,从第 1 个操作字符开始逐位对比:
- 在第一个不相同的操作位置上,字符为
'L'的方案字典序更小(优先级更高)。 - 示例:
"LLRLL"的字典序小于"LRLLL"(在第 2 位上'L''R')。
决定了 (二者必须相等)。为了优先保证字典序最小,先尝试第一步选 L(取 ),找到与 相等的位置 ;若无解,再尝试第一步选 R(取 )。
假设第一步选了 ,以 的匹配位置 为分界点,将剩下的元素划分为两个独立的双端队列:
- 左区间:(左端点 ,右端点 )
- 右区间:(左端点 ,右端点 )
每次从外侧端点( 或 )挑选一个数,匹配内侧端点( 或 )的数。按以下优先级依次尝试:
- 取 匹配 (左区间内部匹配) 前半加
L,后半加L - 取 匹配 (左区间左端 匹配 右区间左端) 前半加
L,后半加R - 取 匹配 (右区间右端 匹配 左区间右端) 前半加
R,后半加L - 取 匹配 (右区间内部匹配) 前半加
R,后半加R
若这四种匹配均无法满足,说明当前初始选择无解。
#include <iostream>#include <vector>#include <string>#include <algorithm>
using namespace std;
int n;vector<int> a;
// 模拟 n-1 步双端队列匹配bool solve(int l1, int r1, int l2, int r2, string &ans_left, string &ans_right) { for (int step = 1; step < n; ++step) { // 1. 优先尝试从左区间的左端 l1 开始匹配 (对应前半段 + 'L') if (l1 <= r1 && l1 < r1 && a[l1] == a[r1]) { // 匹配左区间右端 r1 l1++; r1--; ans_left += 'L'; ans_right += 'L'; } else if (l1 <= r1 && l2 <= r2 && a[l1] == a[l2]) { // 匹配右区间左端 l2 l1++; l2++; ans_left += 'L'; ans_right += 'R'; } // 2. 再尝试从右区间的右端 r2 开始匹配 (对应前半段 + 'R') else if (l2 <= r2 && l1 <= r1 && a[r2] == a[r1]) { // 匹配左区间右端 r1 r2--; r1--; ans_left += 'R'; ans_right += 'L'; } else if (l2 <= r2 && l2 < r2 && a[r2] == a[l2]) { // 匹配右区间左端 l2 r2--; l2++; ans_left += 'R'; ans_right += 'R'; } else { return false; } } return true;}
void process() { cin >> n; a.resize(2 * n + 1); for (int i = 1; i <= 2 * n; ++i) { cin >> a[i]; }
// 情况一:第一步选 L(即取出 a[1]) int p1 = -1; for (int i = 2; i <= 2 * n; ++i) { if (a[i] == a[1]) { p1 = i; break; } }
if (p1 != -1) { // ans_left 记录前半部分(第 1 ~ n 步)的操作,初始选 L (取 a[1]) // ans_right 记录后半部分(第 n+1 ~ 2n 步)的操作 // 这里的 "L" 代表最后一步(第 2n 步)取出 a[p1] 的操作: // 此时序列只剩唯一元素 a[p1],左右端点等价,为满足字典序最小优先记为 'L' // (注:ans_right 在追加完后续操作后会整体 reverse,初始的 "L" 会翻转到最终字符串的末尾) string ans_left = "L", ans_right = "L";
// 左区间 [2, p1-1],右区间 [p1+1, 2*n] if (solve(2, p1 - 1, p1 + 1, 2 * n, ans_left, ans_right)) { // 将倒序收集的后半段(第 2n ~ n+1 步)翻转为正确的正序(第 n+1 ~ 2n 步) reverse(ans_right.begin(), ans_right.end());
cout << ans_left << ans_right << "\n"; return; } }
// 情况二:第一步选 R(即取出 a[2*n]) int p2 = -1; for (int i = 1; i < 2 * n; ++i) { if (a[i] == a[2 * n]) { p2 = i; break; } }
if (p2 != -1) { string ans_left = "R", ans_right = "L"; // 左区间 [1, p2-1],右区间 [p2+1, 2*n-1] if (solve(1, p2 - 1, p2 + 1, 2 * n - 1, ans_left, ans_right)) { reverse(ans_right.begin(), ans_right.end()); cout << ans_left << ans_right << "\n"; return; } }
cout << -1 << "\n";}
int main() { // 快速 IO ios::sync_with_stdio(false); cin.tie(nullptr);
int T; cin >> T; while (T--) { process(); }
return 0;}- 时间复杂度:每一组数据只需移动双指针匹配 n 次,故单组数据复杂度为 O(n)。处理全部测试点耗时大约在几十毫秒以内。
- 空间复杂度:存储数组 a 和答案串,空间复杂度为 O(n)。