1. 題目
題目鏈接:P1439「【模板】最長公共子序列」 。
題目描述
給出 的兩個排列 和 ,求它們的最長公共子序列。
輸入格式
第一行是一個數 。
接下來兩行,每行爲 個數,爲自然數 的一個排列。
輸出格式
一個數,即最長公共子序列的長度。
輸入輸出樣例
輸入 #1
5
3 2 1 4 5
1 2 3 4 5
輸出 #1
3
說明/提示
對於 的數據,;
對於 的數據,。
2. 題解
分析
這是一道 LCS 的模板題,但是如果只用樸素的動態規劃來解,複雜度是 ,結果終究會 TLE。和 LCS 類似的是 LIS,然而 LIS 有 的解法,幸運的是部分 LCS 問題可以用 LIS 來解。
在 LCS 中,兩個序列中的元素僅表示一種符號,用來判定是否相等的符號,對於符號背後具體的數值對 LCS 的結果並沒有影響。
在 LIS 中,是需要考慮序列元素的數值大小關係的。
若 LCS 的兩個序列中的其中一個元素互異,則可以用 LIS 來解。在此類 LCS 中,不防假設序列 中元素是互異的,設序列 的長度爲 ,則我們可以考慮將 的元素按照出現順序依次映射到 上,從而得到 中元素與數值的一一對應關係。然後根據 元素的映射表,來計算序列 元素對應的映射值;對於在 中存在而不在 中存在的元素,直接捨棄即可,因爲它們必然不會出現在 LCS 中。如此,映射完 得到的數值序列設爲 ,其長度爲 。則此時只需要計算序列 的 LIS 即可。這是因爲序列 映射後的序列是一個遞增的數值序列,因此 和 的公共子序列也是遞增子序列,最長公共子序列也是最長遞增子序列,即序列 的最長遞增子序列。
針對這道題而言,由於兩個序列都是 的排列,因此可以轉爲 LIS 問題來求解。
代碼
#include <bits/stdc++.h>
using namespace std;
// 最長遞增子序列
struct LIS{
#ifndef _LIS_
#define ll int
#define MAXN 100005
#endif
ll n;
ll A[MAXN];
LIS(): n(0) {}
// 二分+棧求 LIS 長度
// 複雜度 O(nlogn)
ll length() {
ll cntn = n;
ll *seqa = A;
vector <ll> lis;
lis.push_back(seqa[0]);
for(ll i = 1; i < cntn; ++i) {
if(seqa[i] > lis.back()) {
lis.push_back(seqa[i]);
} else {
ll pos = lower_bound(lis.begin(), lis.end(), seqa[i]) - lis.begin();
lis[pos] = min(lis[pos], seqa[i]);
}
}
return lis.size();
}
};
int main()
{
int n;
int P[MAXN];
LIS lis;
scanf("%d", &n);
lis.n = n;
for(int i = 0; i < n; ++i) {
int a;
scanf("%d", &a);
P[a] = i;
}
for(int i = 0; i < n; ++i) {
int b;
scanf("%d", &b);
lis.A[i] = P[b];
}
printf("%d\n", lis.length());
return 0;
}