Codeforces Round #620 (Div. 2)(D. Shortest and Longest LIS)(O(n log n)的最长上升子序列或者贪心)

Codeforces Round #620 (Div. 2)(D. Shortest and Longest LIS)(O(n log n)的最长上升子序列或者贪心)

time limit per test3 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
judge:点我跳转

Description

Gildong recently learned how to find the longest increasing subsequence (LIS) in O ( n   l o g   n ) O(n\ log\ n) O(n log n) time for a sequence of length n n n. He wants to test himself if he can implement it correctly, but he couldn’t find any online judges that would do it (even though there are actually many of them). So instead he’s going to make a quiz for you about making permutations of n n n distinct integers between 1 1 1 and n n n, inclusive, to test his code with your output.

The quiz is as follows.

Gildong provides a string of length n − 1 n−1 n1, consisting of characters ‘<’ and ‘>’ only. The i i i-th (1-indexed) character is the comparison result between the i i i-th element and the i + 1 i+1 i+1-st element of the sequence. If the i i i-th character of the string is ‘<’, then the i i i-th element of the sequence is less than the i + 1 i+1 i+1-st element. If the i i i-th character of the string is ‘>’, then the i i i-th element of the sequence is greater than the i + 1 i+1 i+1-st element.

He wants you to find two possible sequences (not necessarily distinct) consisting of n n n distinct integers between 1 1 1 and n n n, inclusive, each satisfying the comparison results, where the length of the LIS of the first sequence is minimum possible, and the length of the LIS of the second sequence is maximum possible.

Input

Each test contains one or more test cases. The first line contains the number of test cases t ( 1 ≤ t ≤ 1 0 4 ) t (1≤t≤10^4) t(1t104).

Each test case contains exactly one line, consisting of an integer and a string consisting of characters ‘<’ and ‘>’ only. The integer is n ( 2 ≤ n ≤ 2 ⋅ 1 0 5 ) n (2≤n≤2⋅10^5) n(2n2105), the length of the permutation you need to find. The string is the comparison results explained in the description. The length of the string is n − 1 n−1 n1.

It is guaranteed that the sum of all n in all test cases doesn’t exceed 2 ⋅ 1 0 5 2⋅10^5 2105.

Output

For each test case, print two lines with n n n integers each. The first line is the sequence with the minimum length of the LIS, and the second line is the sequence with the maximum length of the LIS. If there are multiple answers, print any one of them. Each sequence should contain all integers between 1 1 1 and n n n, inclusive, and should satisfy the comparison results.

It can be shown that at least one answer always exists.

Example

input
3
3 <<
7 >><>><
5 >>><
output
1 2 3
1 2 3
5 4 3 7 2 1 6
4 3 1 7 5 2 6
4 3 2 1 5
5 4 2 1 3

Note

In the first case, 1   2   3 1\ 2\ 3 1 2 3 is the only possible answer.

In the second case, the shortest length of the LIS is 2 2 2, and the longest length of the LIS is 3 3 3. In the example of the maximum LIS sequence, ‘4’ ‘3’ ‘1’ ‘7’ ‘5’ ‘2’ ‘6’ can be one of the possible LIS.

题解

题意

给你一个表示大小关系的序列,序列里只包含’<‘和’>’,它表示的是一个序列里相邻的数字的大小关系。题目让你找出符合这个大小关系同时有着最短的“最长上升子序列”的序列,和符合这个大小关系同时有着最长的“最长上升子序列”的序列。

有点绕,分析一个样例,比如
7
>><>><
在这个样例里,5 4 3 7 2 1 6这个序列有着最短的"最长上升子序列",它的"最长上升子序列"的长度是2;4 3 1 7 5 2 6这个序列有着最长的"最长上升子序列",它的"最长上升子序列"的长度是3。

这道题真的很有意思。本来很简单的一道题,思路我想大家都想到了,那就是求最短的序列时尽可能把值较大的数往前放,求最长的序列时尽可能把值较大的数往后放。基于这两个准则分别找到符合题目所给大小关系的一组序列即是答案。

那么具体来说就是先让“表示最短的”序列要把数字大的往前放,先初始化为5 4 3 2 1,然后不断地找字符串里的’<’,让一段只有’<'的区间里的数字对调来满足大小关系,一遍修改之后就是答案;反之同理。是不是很简单。

不过比赛时我没有想到这个思路,而是倒推 基于二分的 O ( n   l o g   n ) O(n\ log\ n) O(n log n) 的最长上升子序列 同样能 O ( n ) O(n) O(n) 求得答案,虽然复杂了点,但思路还是很实用的,不止在这道题上能用。

不会这种方法的同学可以补一补,会的话直接跳过


参考动态规划-最长上升(下降)子序列的O(n logn)写法(基于二分),我们学习了一个新的求最长上升子序列的方法。


反过来,假设有一个序列 a a a ,下标分别是0 1 2 3 4 5 6,值满足<<>><<的大小关系。

先求最长的“最长上升子序列”:

  1. 构造一个数组 b b b ,b数组的长度就是“最长上升子序列”的长度。初始时只有一个元素是 a [ 0 ] a[0] a[0]
  2. 第一个字符是’<’,那么说明 a [ 1 ] > a [ 0 ] a[1]>a[0] a[1]>a[0] ,这样 a [ 1 ] a[1] a[1] 就能和前面的数字连起来组成上升的序列,按照动态规划-最长上升(下降)子序列的O(n logn)写法(基于二分)的思想, a [ 1 ] a[1] a[1] 比数组 b b b 里最大的元素都大,说明 a [ 1 ] a[1] a[1] 比之前的元素都大,可以组成上升序列,把 a [ 1 ] a[1] a[1] 压入 b b b 数组, b b b 数组为 a[0] a[1]
  3. 第二个字符是’<’,说明 a [ 2 ] a[2] a[2] 比之前的元素都大,可以组成上升子序列,把 a [ 2 ] a[2] a[2] 压入 b b b 数组, b b b 数组为 a[0] a[1] a[2]
  4. 第三个字符是’>’,说明 a [ 3 ] < a [ 2 ] a[3]<a[2] a[3]<a[2] ,这时需要在之前的数字里找到大于 a [ 3 ] a[3] a[3] 的数字,用 a [ 3 ] a[3] a[3] 代替它和前面的数组成上升序列,但同时我们希望序列 a a a 靠前的元素值比较大,所以假设 a [ 3 ] a[3] a[3] 只小于 a [ 2 ] a[2] a[2] ,那么拿 a [ 3 ] a[3] a[3] 代替 a [ 2 ] a[2] a[2] ,数组 b b b 变成了a[0] a[1] a[3]
  5. 第四个字符是’>’,说明 a [ 4 ] < a [ 3 ] a[4]<a[3] a[4]<a[3] ,这时需要在之前的数字里找到大于 a [ 4 ] a[4] a[4] 的数字,用 a [ 4 ] a[4] a[4] 代替它和前面的数组成上升序列,但同时我们希望序列a前面的元素值比较大,所以假设 a [ 4 ] a[4] a[4] 只小于 a [ 3 ] a[3] a[3] ,那么拿 a [ 4 ] a[4] a[4] 代替 a [ 3 ] a[3] a[3] ,数组 b b b 变成了a[0] a[1] a[4]
  6. 第五个字符是’<’,说明 a [ 5 ] a[5] a[5] 比之前的元素都大,可以组成上升子序列,把 a [ 5 ] a[5] a[5] 压入 b b b 数组, b b b 数组为a[0] a[1] a[4] a[5]
  7. 第六个字符是’<’,说明 a [ 6 ] a[6] a[6] 比之前的元素都大,可以组成上升子序列,把 a [ 6 ] a[6] a[6] 压入 b b b 数组, b b b 数组为a[0] a[1] a[4] a[5] a[6]

这个时候 b b b 数组的长度是 5 5 5那么这个字符串能求出来的最长的“最长上升子序列”的长度就是 5 5 5 .然后怎么求序列具体的值呢?我们把数组 b b b 变成一个矩阵,也就是二维数组,把每个“代替”操作变成压入对应的子数组,那么第四步的矩阵 b b b 就是这样的:

a[0] a[1] a[2]
          a[3]

第五步的矩阵 b b b 是这样的:

a[0] a[1] a[2]
          a[3]
          a[4]

第7步后的矩阵 b b b 是这样的:

a[0] a[1] a[2] a[5] a[6]
          a[3]
          a[4]

我们从后往前把 a [ i ] a[i] a[i] 换成具体的值,按照从后往前从上往下依次递减的方式一一对应:

1 2 5 6 7
    4
    3

得到的序列就是1 2 5 4 3 6 7,它的最长上升子序列的长度是 5 5 5

要求最短的“最长上升子序列”的话,从第四步稍微更改一个步骤,既然要求最短的,那么我们希望 a a a 数组靠前的值尽可能小一些,后面的值尽可能大一些,所以:

  1. 这时 a [ 3 ] < a [ 2 ] a[3]<a[2] a[3]<a[2],我们假设 a [ 3 ] a[3] a[3] 小于前面的任何值,那么 a [ 3 ] a[3] a[3] 就会归入 b [ 0 ] b[0] b[0] 数组里,矩阵 b b b 现在是这样的:
a[0] a[1] a[2]
a[3]
  1. 这时 a [ 4 ] < a [ 3 ] a[4]<a[3] a[4]<a[3],我们同样假设 a [ 4 ] a[4] a[4] 小于前面的任何值,那么那么 a [ 4 ] a[4] a[4] 就会归入 b [ 0 ] b[0] b[0] 数组里,矩阵 b b b 现在是这样的:
a[0] a[1] a[2]
a[3]
a[4]
  1. 这时候 a [ 5 ] > a [ 4 ] a[5]>a[4] a[5]>a[4] ,我们假设 a [ 5 ] 只 大 于 a [ 4 ] a[5]只大于a[4] a[5]a[4] ,而小于其他任何值,那么 a [ 5 ] a[5] a[5] 就会归入 b [ 1 ] b[1] b[1] 里,现在矩阵 b b b 是这样的:
a[0] a[1] a[2]
a[3] a[5]
a[4]

第七步亦然,第七步结束后,矩阵 b b b 是这样的:

a[0] a[1] a[2]
a[3] a[5] a[6]
a[4]

同样按照从后往前从上往下依次递减的方式一一对应:

3 5 7
2 4 6
1

得到的序列就是3 5 7 2 1 4 6,它的最长上升子序列的长度是 3 3 3

大神思路

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int t, n, p[200005];
char s[200005];
int main() {
	scanf("%d", &t);
	while (t--) {
		scanf("%d%s", &n, s + 1);
		for (int i = 1; i <= n; i++) p[i] = n - i + 1;
		for (int i = 1; i < n;) {
			int j = i;
			while (s[j] == '<') j++;
			reverse(p + i, p + j + 1);
			i = j + 1;
		}
		for (int i = 1; i <= n; i++) printf("%d ", p[i]);
		printf("\n");
		for (int i = 1; i <= n; i++) p[i] = i;
		for (int i = 1; i < n;) {
			int j = i;
			while (s[j] == '>') j++;
			reverse(p + i, p + j + 1);
			i = j + 1;
		}
		for (int i = 1; i <= n; i++) printf("%d ", p[i]);
		printf("\n");
	}
	return 0;
}

我的思路

#include <bits/stdc++.h>
#define maxn 200005
#define _for(i, a) for(int i = 0; i < (a); ++i)
#define mem0(a) memset(a, 0, sizeof(a))
using namespace std;

int T, n;
string s;
vector< vector< int > > a, a_;
int num[maxn], num_[maxn];

void init() {
	a.clear();
	a_.clear();
	mem0(num);
	mem0(num_);
}

void sol() {
	init();
	a.push_back(vector< int >());
	a_.push_back(vector< int >());
	a[0].push_back(0);
	a_[0].push_back(0);
	int tem = 0, pos = 1;
	int tem_ = 0, pos_ = 1;
	_for(i, n - 1) {
		if (s[i] == '>') {
			a[0].push_back(pos++);	//最短
			tem = 0;

			a_[tem_].push_back(pos_++);	//最长
		}
		else {
			if (tem + 1 >= a.size()) a.push_back(vector<int>());	//最短
			a[++tem].push_back(pos++);

			if (tem_ + 1 >= a_.size()) a_.push_back(vector<int>());	//最长
			a_[++tem_].push_back(pos_++);
		}
	}
	tem_ = tem = s.size() + 1;
	for (int i = a.size() - 1; i >= 0; --i) {	//最短
		_for(j, a[i].size()) num[a[i][j]] = tem--;
	}
	for (int i = 0, l = s.size() + 1; i < l; ++i) cout << num[i] << (i == l - 1 ? "\n" : " ");

	for (int i = a_.size() - 1; i >= 0; --i) {	//最长
		_for(j, a_[i].size()) num_[a_[i][j]] = tem_--;
	}
	for (int i = 0, l = s.size() + 1; i < l; ++i) cout << num_[i] << (i == l - 1 ? "\n" : " ");
}

int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	//freopen("in.txt", "r", stdin);

	while (cin >> T) {
		_for(i, T) {
			cin >> n >> s;
			sol();
		}
	}
	return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值