0
点赞
收藏
分享

微信扫一扫

P1439 【模板】最长公共子序列

进击的铁雾 2022-03-21 阅读 42
c++算法

先從樸素的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;
}
举报

相关推荐

0 条评论