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 log n)O(n\ log\ n) time for a sequence of length nn. 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 nn distinct integers between 11 and nn, inclusive, to test his code with your output.

The quiz is as follows.

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

He wants you to find two possible sequences (not necessarily distinct) consisting of nn distinct integers between 11 and nn, 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(1t104)t (1≤t≤10^4).

Each test case contains exactly one line, consisting of an integer and a string consisting of characters ‘<’ and ‘>’ only. The integer is n(2n2105)n (2≤n≤2⋅10^5), 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 n1n−1.

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

Output

For each test case, print two lines with nn 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 11 and nn, 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 31\ 2\ 3 is the only possible answer.

In the second case, the shortest length of the LIS is 22, and the longest length of the LIS is 33. 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 log n)O(n\ log\ n) 的最長上升子序列 同樣能 O(n)O(n) 求得答案,雖然複雜了點,但思路還是很實用的,不止在這道題上能用。

不會這種方法的同學可以補一補,會的話直接跳過


參考動態規劃-最長上升(下降)子序列的O(n logn)寫法(基於二分),我們學習了一個新的求最長上升子序列的方法。


反過來,假設有一個序列 aa ,下標分別是0 1 2 3 4 5 6,值滿足<<>><<的大小關係。

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

  1. 構造一個數組 bb ,b數組的長度就是“最長上升子序列”的長度。初始時只有一個元素是 a[0]a[0]
  2. 第一個字符是’<’,那麼說明 a[1]>a[0]a[1]>a[0] ,這樣 a[1]a[1] 就能和前面的數字連起來組成上升的序列,按照動態規劃-最長上升(下降)子序列的O(n logn)寫法(基於二分)的思想,a[1]a[1] 比數組 bb 裏最大的元素都大,說明 a[1]a[1] 比之前的元素都大,可以組成上升序列,把 a[1]a[1] 壓入 bb 數組, bb 數組爲 a[0] a[1]
  3. 第二個字符是’<’,說明 a[2]a[2] 比之前的元素都大,可以組成上升子序列,把 a[2]a[2] 壓入 bb 數組, bb 數組爲 a[0] a[1] a[2]
  4. 第三個字符是’>’,說明 a[3]<a[2]a[3]<a[2] ,這時需要在之前的數字裏找到大於 a[3]a[3] 的數字,用 a[3]a[3] 代替它和前面的數組成上升序列,但同時我們希望序列 aa 靠前的元素值比較大,所以假設 a[3]a[3] 只小於 a[2]a[2] ,那麼拿 a[3]a[3] 代替 a[2]a[2] ,數組 bb 變成了a[0] a[1] a[3]
  5. 第四個字符是’>’,說明 a[4]<a[3]a[4]<a[3] ,這時需要在之前的數字裏找到大於 a[4]a[4] 的數字,用 a[4]a[4] 代替它和前面的數組成上升序列,但同時我們希望序列a前面的元素值比較大,所以假設 a[4]a[4] 只小於 a[3]a[3] ,那麼拿 a[4]a[4] 代替 a[3]a[3] ,數組 bb 變成了a[0] a[1] a[4]
  6. 第五個字符是’<’,說明 a[5]a[5] 比之前的元素都大,可以組成上升子序列,把 a[5]a[5] 壓入 bb 數組,bb 數組爲a[0] a[1] a[4] a[5]
  7. 第六個字符是’<’,說明 a[6]a[6] 比之前的元素都大,可以組成上升子序列,把 a[6]a[6] 壓入 bb 數組, bb 數組爲a[0] a[1] a[4] a[5] a[6]

這個時候 bb 數組的長度是 55那麼這個字符串能求出來的最長的“最長上升子序列”的長度就是 55 .然後怎麼求序列具體的值呢?我們把數組 bb 變成一個矩陣,也就是二維數組,把每個“代替”操作變成壓入對應的子數組,那麼第四步的矩陣 bb 就是這樣的:

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

第五步的矩陣 bb 是這樣的:

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

第7步後的矩陣 bb 是這樣的:

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

我們從後往前把 a[i]a[i] 換成具體的值,按照從後往前從上往下依次遞減的方式一一對應:

1 2 5 6 7
    4
    3

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

要求最短的“最長上升子序列”的話,從第四步稍微更改一個步驟,既然要求最短的,那麼我們希望 aa 數組靠前的值儘可能小一些,後面的值儘可能大一些,所以:

  1. 這時 a[3]<a[2]a[3]<a[2],我們假設 a[3]a[3] 小於前面的任何值,那麼 a[3]a[3] 就會歸入 b[0]b[0] 數組裏,矩陣 bb 現在是這樣的:
a[0] a[1] a[2]
a[3]
  1. 這時 a[4]<a[3]a[4]<a[3],我們同樣假設 a[4]a[4] 小於前面的任何值,那麼那麼 a[4]a[4] 就會歸入 b[0]b[0] 數組裏,矩陣 bb 現在是這樣的:
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[5] 就會歸入 b[1]b[1] 裏,現在矩陣 bb 是這樣的:
a[0] a[1] a[2]
a[3] a[5]
a[4]

第七步亦然,第七步結束後,矩陣 bb 是這樣的:

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,它的最長上升子序列的長度是 33

大神思路

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章