luogu_P7915 [CSP-S 2021] 回文

题意

对于长度为 2 n 2n 2n的序列 a a a 1 ∼ n 1\sim n 1n各出现了 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 <cstdio>

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");
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值