概念
Loggest Increasing Subsequence:最長上升子序列
Loggest Common Subsequnce: 最長公共子序列(兩個或多個序列的問題)
概念就不解釋了,,,大家應該都懂
解法
O(nlog(n))的解法
這種解法是貪心+二分的思想。實際並不難,比下面的O(n^2)複雜度的解法還更容易理解。
這種算法的思想大致如下:
現在有 4 2 3 8 9 7 這個序列,下面以他爲例,說明求最長子序列的長度。
首先定義一個數組,保存這個最長的上升子序列的每一個元素,設爲dp數組。
下面我們就要依次往裏面放元素了:
第一個元素是4,直接放進dp。 || 當前dp中元素的情況:4
第二個元素是2,比4小。把dp中的2替換成4.(下面介紹爲什麼)||當前dp中元素的情況:2
第三個元素是3,比2大,放到dp數組中2的後面 ||當前dp中元素的情況:2 3
第四個元素是8,比3大,放到dp數組中3的後面 ||當前dp中元素的情況:2 3 8
第五個元素是9,比8大,放到dp數組中8的後面 ||當前dp中元素的情況:2 3 8 9
第六個元素是7,替換dp數組中的8 ||當前dp中元素的情況:2 3 7 9
最後結果:最長公共子序列的長度是4.
結論:每次進來一個數 ,他總是替換dp數組從左往右第一個大於它的數。
而存到dp數組中的元素,不一定就是真實的元素。上面的例子的最長上升子序列應該是 2 3 8 9.所以這個數組只是個僞序列,真正有用的是數組的長度。
我現在只能做到這裏,證明我也不會。。。。。。。。。但就是這樣子。
O(n^2)做法
這種做法,各種數據結構的書上應該都有吧。只要找到狀態轉移方程:
dp[i]=max(dp[j])+1; //條件是1=<j<i 並且 a[i]>a[j] .
很容易就可以寫出代碼
LCS轉LIS
這便是這篇博客的意義所在了,LIS算法有O(nlogn)的算法,而LCS沒有。
看這樣一道題:P1439
題目描述
給出 1,2,…,n 的兩個排列P1 和 P2 ,求它們的最長公共子序列。
輸入格式
第一行是一個數 n。
接下來兩行,每行爲 n個數,爲自然數 1,2,…,n 的一個排列。
輸出格式
一個數,即最長公共子序列的長度。
輸入輸出樣例
輸入 #1複製
5 3 2 1 4 5 1 2 3 4 5
輸出 #1複製
3
說明/提示
- 對於50% 的數據, n≤10^3;
- 對於100% 的數據, n≤10^5。
分析:
這道題n達到了10^5,用經典的LCS解法,顯然超時。這就需要將LCS向LIS轉化
用到的思想:離散化。
舉例:如下兩個序列
4 2 3 8 9
8 4 2 3 9
做一步離散化處理,不考慮每個元素具體的值,用新數組每個元素表示原數組中元素所在的位置
1 2 3 4 5
4 1 2 3 5
說明:上述第一行表示原序列的4 2 3 8 9的每一個位置,也是就8在原序列第一個序列中的第一位、4在第1位、2在第二位、3在第三位、9在第5位。
---
理解上述的操作之後,下面觀察生成的第二個序列的規律:只要求生成的第二個序列的最長上升序列,就可以得到LCS
ok,代碼如下,僅供參考
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX = 5e5+5;
int N;
int a[MAX],b[MAX];
int dp[MAX];
int belong[MAX*100]; //離散化的關鍵
int main() {
cin >> N;
for (int i = 1; i <= N; ++i) {
cin >> a[i];
belong[a[i]]=i;
}
for (int i = 1; i <= N; ++i) {
cin >> b[i];
b[i]=belong[b[i]];
}
int len = 0;
memset(dp,0,sizeof(dp));
for (int i = 1; i <= N; ++i) {
if(dp[len]<b[i]) {
dp[++len]=b[i];
} else {
*lower_bound(dp+1,dp+len+1,b[i])=b[i]; //二分查找,降低時間複雜度
}
}
cout << len;
}