先從樸素的dp入手。
最直接的狀態設計就是dp[i][j] = a的前i個元素和b的前j個元素所組成的LCS長度。
兩種情況
1. a[i] == b[i]
2. a[i] != b[i]
考慮情況1:
a[i] 和 b[i] 一樣,那麼最優的情況必定是繼承圖片1中紅色圈圈的部分,再加上1。紅色圈圈其實就是i前的所有狀態中的最優值,他是一路傳過來的。
考慮情況2:
那不一樣的話,還是跟情況1一樣嗎? 由於不一樣你要考慮究竟用a 作為結尾和 b 作為結尾誰長一點,所以基本上我們的dp公式已經出來了。
由於i要和1到n的j進行匹配,這個算法需要n^2的時間。
我們考慮優化一下。
由於他們是排列不一樣的兩個數組,能否有一種方法是讓a和b之間產生點關係,讓整個問題變得簡單呢?
利用一下樹狀數組,我們讓a上個編號,讓b跟著a的編號覆蓋掉之前的值。
他們的LCS會改變,經歷上面的變化?
並不會,因為b數組也做了相同的變化,LCS不會有改變。
但是如果a的編號是全部有序的,那麼是不是說在b找到LIS就可以說是他們的LCS了呢。
這種思想能打開腦洞!!!
#include <iostream>
using namespace std;
int a[100002], b[100002];
int n, ans = 1;
int xt[100002], yt[100002];
void transfer(){
for(int i = 1; i <= n; i++){
xt[a[i]] = i;
}
for(int i = 1; i <= n; i++){
yt[i] = xt[b[i]];
}
}
int bit[100004];
int binarySearch(int rght, int target){
int lft = 1;
while(lft <= rght){
int mid = (rght + lft) >> 1;
if(bit[mid] < target)lft = mid + 1;
else rght = mid - 1;
}
return lft;
}
int findLIS(){
bit[1] = yt[1];
for(int i = 2; i <= n; i++){
if(bit[ans] < yt[i]){
bit[++ans] = yt[i];
}else{
bit[binarySearch(ans, yt[i])] = yt[i];
}
}
return ans;
}
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
for(int i = 1; i <= n; i++){
cin >> b[i];
}
transfer();
cout << findLIS() << endl;
return 0;
}