[xdoj1233]Glory and LCS

题意:求两个排列的最长公共子序列n<=1e5

解题关键:转化为LIS。

最长公共子序列 的 nlogn 的算法本质是 将该问题转化成 最长增序列(LIS),因为 LIS 可以用nlogn实现,所以求LCS的时间复杂度降低为 nlogn。

1. 转化:将LCS问题转化成LIS问题。

               假设有两个序列 s1[ 1~6 ] = { a, b, c , a, d, c }, s2[ 1~7 ] = { c, a, b, e, d, a, b }。

               记录s1中每个元素在s2中出现的位置, 再将位置按降序排列, 则上面的例子可表示为:

               loc( a)= { 6, 2 }, loc( b ) = { 7, 3 }, loc( c ) = { 1 }, loc( d ) = { 5 }。

               将s1中每个元素的位置按s1中元素的顺序排列成一个序列s3 = { 6, 2, 7, 3, 1, 6, 2, 5, 1 }。

               在对s3求LIS得到的值即为求LCS的答案

2.求LIS的 nlogn 的算法:

               覆盖:是序列s的几个不相交的降序列,它们包含了s中的所有元素,降序列的个数为c。

               最小覆盖:c值最小的覆盖。

              定理:序列s的最长增序列等于最小覆盖。

              于是:求s的最长增序列转化成求s的最小覆盖。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<cstdlib>
 5 #include<iostream>
 6 #include<cmath>
 7 #include<string>
 8 #define inf 0x3f3f3f3f
 9 using namespace std;
10 typedef long long ll;
11 const int maxn=1005000;
12 int n;
13 inline int read(){
14     char k=0;char ls;ls=getchar();for(;ls<'0'||ls>'9';k=ls,ls=getchar());
15     int x=0;for(;ls>='0'&&ls<='9';ls=getchar())x=(x<<3)+(x<<1)+ls-'0';
16     if(k=='-')x=0-x;return x;
17 }
18 int ind[maxn];
19 int a[maxn],b[maxn],dp[maxn],c[maxn];
20 int main(){
21     int t;
22     t=read();
23     while(t--){
24         n=read();
25         for(int i=0;i<n;i++) a[i]=read();
26         for(int i=0;i<n;i++) b[i]=read(),ind[b[i]]=i;
27         for(int i=0;i<n;i++) c[i]=ind[a[i]];
28         fill(dp,dp+n,inf);
29         for(int i=0;i<n;i++){
30             *lower_bound(dp,dp+n,c[i])=c[i];
31         }
32         printf("%d
",int(lower_bound(dp,dp+n,inf)-dp));
33     }
34     return 0;
35 }
原文地址:https://www.cnblogs.com/elpsycongroo/p/7429414.html