P1439 【模板】最長公共子序列
阿新 • • 發佈:2020-09-08
最長公共子序列
題目描述
給出 \(1,2,\ldots,n\) 的兩個排列 \(P_1\) 和 \(P_2\) ,求它們的最長公共子序列。
輸入格式
第一行是一個數 \(n\)。
接下來兩行,每行為 \(n\) 個數,為自然數 \(1,2,\ldots,n\) 的一個排列。
輸出格式
一個數,即最長公共子序列的長度。
輸入輸出樣例
輸入 #1
5
3 2 1 4 5
1 2 3 4 5
輸出 #1
3
說明/提示
-
對於 50% 的資料, \(n \le 10^3\);
-
對於 100% 的資料, \(n \le 10^5\)。
我做這道題的時候心路歷程是這樣的:
這不是個模板題嗎,水過去了 ---> wdnmd 這怎麼 \(TLE\)
先給出O(\(n^2\))的轉移方程吧:
設 \(f[i][j]\) 表示 第一個串的前 \(i\) 位,第二個串的前 \(j\) 位的最長公共子序列的長度。
那麼就有轉移:
- \(a_i \neq b_j\) \(f[i][j] = max(f[i-1][j],f[i][j-1],f[i][j])\)
- \(a_i == b_j\) \(f[i][j] = max(f[i-1][j],f[i][j-1],f[i][j])\)
Code
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int n; int a[100010],b[100010]; int f[1010][1010]; int main(){ scanf("%d",&n); for(int i = 1; i <= n; i++) scanf("%d",&a[i]); for(int j = 1; j <= n; j++) scanf("%d",&b[j]); f[0][1] = f[1][0] = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { f[i][j] = max(f[i][j],max(f[i-1][j],f[i][j-1])); if(a[i] == b[j]) { f[i][j] =max(f[i][j], f[i-1][j] + 1); } } } printf("%d",f[n][n]); return 0; }
可這道題 \(O(n^2)\) 的資料顯然過不去。
我們這就需要仔細觀察一下這道題,然後你就會發現他有一個性質就是給出的兩個序列都是排列。
這意味著什麼?,也就是說 \(p_1\) 中的數一定·會在 \(p_2\) 中出現過。
所以,我們在 \(p_1\) 中排名靠前的數在 \(p_2\) 中排名也要靠前。
我們對 \(p_1\) 中得數求一下他在 \(p_2\) 出現的位置,然後求一個最長上升子序列就完事了。
Code:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N = 1e5+10; int n,ans,a[N],b[N],pos[N],tr[N]; int lowbit(int x){return x & -x;} inline int read() { int s = 0,w = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();} return s * w; } void chenge(int x,int val)//樹狀陣列查詢字首最大值 { for(; x <= N-5; x += lowbit(x)) tr[x] = max(tr[x],val); } int ask(int x) { int res = 0; for(; x; x -= lowbit(x)) res = max(res,tr[x]); return res; } int main() { n = read(); for(int i = 1; i <= n; i++) a[i] = read(); for(int i = 1; i <= n; i++) { b[i] = read(); pos[b[i]] = i; } for(int i = 1; i <= n; i++) a[i] = pos[a[i]];//記錄一下每個數在b中出現的位置 for(int i = 1; i <= n; i++) { int tmp = ask(a[i]-1) + 1; chenge(a[i],tmp); ans = max(ans,tmp); } printf("%d\n",ans); return 0; }