对于长度为 2 n 2n 2n的序列 a a a, 1 ∼ n 1\sim n 1∼n各出现了 2 2 2次。
进行如下两种操作:
1、将序列 a a a的开头元素加到 b b b的末尾,并从 a a a中移除。
2、将序列 a a a的末尾元素加到 b b b的末尾,并从 a a a中移除。
求出一个操作序列使得 b b b为一个回文串,且操作序列的字典序最小。
先移除左边(对于右边也是如此)的数字,然后这个数字剩下的另一个一定是最后一个移除的(满足回文),那么倒数第二个移除的就应该是它左右两个中的其中之一(因为只能从左或者从右移除)。
于是就可以通过它左右的两个数字判断出下一个应该取出的数字是哪个(即它的另一个应满足在序列最左或最右端)。
之后的数字以此类推,发现是一个从里面往外扩和外面往里缩的操作。
就用四个指针做一下就好了,因为要求操作字典序最小,所以当出现左右都可移除时,先移除左边。而每个数字都只会被扫一次,这样复杂度就是 O ( N ) O(N) O(N)
#include
const int N = 1e6 + 1;
int t, n, top, flag;
int a[N], p[N][2];
char st[N];
void dfs(int l1, int r1, int l2, int r2) {
if (l1 + 1 >= l2 && r2 + 1 >= r1) {
flag = 1;
for (int i = 1; i < n; i++)
putchar(st[i]);
printf("L\n");
return;
}
if (p[a[l1 + 1]][1] == l2 - 1 || p[a[l1 + 1]][1] == r2 + 1) {
st[++top] = 'L';
if (p[a[l1 + 1]][1] == l2 - 1)
st[n - top + 1] = 'L', dfs(l1 + 1, r1, l2 - 1, r2);
else
st[n - top + 1] = 'R', dfs(l1 + 1, r1, l2, r2 + 1);
top--;
} else if (p[a[r1 - 1]][0] == l2 - 1 || p[a[r1 - 1]][0] == r2 + 1) {
st[++top] = 'R';
if (p[a[r1 - 1]][0] == l2 - 1)
st[n - top + 1] = 'L', dfs(l1, r1 - 1, l2 - 1, r2);
else
st[n - top + 1] = 'R', dfs(l1, r1 - 1, l2, r2 + 1);
top--;
}
}
int main() {
scanf("%d", &t);
while (t--) {
top = flag = 0;
scanf("%d", &n);
n <<= 1;
for (int i = 1; i <= n; i++)
p[i][0] = p[i][1] = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if (!p[a[i]][0])
p[a[i]][0] = i;
else
p[a[i]][1] = i;
}
st[++top] = 'L';
dfs(1, n + 1, p[a[1]][1], p[a[1]][1]);
if (flag)
continue;
top = 0;
st[++top] = 'R';
dfs(0, n, p[a[n]][0], p[a[n]][0]);
if (flag)
continue;
printf("-1\n");
}
}