P1439 【模板】最长公共子序列
题目描述
给出 1 , 2 , … , n 1,2,\ldots,n 1,2,…,n 的两个排列 P 1 P_1 P1 和 P 2 P_2 P2 ,求它们的最长公共子序列。
输入格式
第一行是一个数 n n n。
接下来两行,每行为 n n n 个数,为自然数 1 , 2 , … , n 1,2,\ldots,n 1,2,…,n 的一个排列。
输出格式
一个数,即最长公共子序列的长度。
样例 #1
样例输入 #1
5
3 2 1 4 5
1 2 3 4 5
样例输出 #1
3
提示
- 对于 50 % 50\% 50% 的数据, n ≤ 1 0 3 n \le 10^3 n≤103;
- 对于 100 % 100\% 100% 的数据, n ≤ 1 0 5 n \le 10^5 n≤105。
题解
递推式(状态转移方程):
f[i][j]=Max(f[i-1][j],f[i][j]),a[i]!=b[j]
f[i][j]=f[i-1][j-1]+1,a[i]=b[j]
边界条件:
f[0][j]=0;
f[i][0]=0;#include<bits/stdc++.h> #define ll long long #define Int __int128 #define pb push_back #define eb emplace_back #define ff first #define ss second #define M1 20 #define M2 1010 #define M3 5005 #define RI register int #define ull unsigned long long #define isc ios::sync_with_stdio(0);cin.tie(0),cout.tie(0); using namespace std; const int inf=0x3f3f3f3f; const int mod=1e9+7; const int N=1e5+10; typedef pair<ll,ll> pll; typedef pair<int,int>pii; int gcd(int a,int b) { while((a%=b)&&(b%=a)); return a+b; }//最大公约数函数 int ksm(int a,int b,int p=mod) { int ans=1; while(b) { if(b&1)ans=ans*a%p; b>>=1; a=a*a%p; } return ans; }//快速幂函数 int n,a[N],b[N],f[M3][M3]; void solve(){ cin>>n; for(int i=1;i<=n;++i)cin>>a[i]; for(int i=1;i<=n;++i)cin>>b[i]; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j){ if(a[i]==b[j])f[i][j]=f[i-1][j-1]+1; else f[i][j]=max(f[i-1][j],f[i][j-1]); } cout<<f[n][n]; } int main(){ int t=1; //cin>>t; while(t--)solve(); }
对于洛谷P1439而言,不仅是卡上面的朴素算法,也考察到了全排列的性质:
对于这个题而言,朴素算法是n2的,会被105卡死,所以我们可以考虑nlogn的做法:
因为两个序列都是n的全排列,那么两个序列元素互异且相同,也就是说只是位置不同罢了,那么我们通过一个map数组将A序列的数字在B序列中的位置表示出来——
因为最长公共子序列是按位向后比对的,所以a序列每个元素在b序列中的位置如果递增,就说明b中的这个数在a中的这个数整体位置偏后,可以考虑纳入LCS——那么就可以转变成nlogn求用来记录新的位置的map数组中的LIS。
关于为什么可以转化成LIS问题,这里提供一个解释。
A:3 2 1 4 5
B:1 2 3 4 5
我们不妨给它们重新标个号:把3标成a,把2标成b,把1标成c……于是变成:
A: a b c d e
B: c b a d e这样标号之后,LCS长度显然不会改变。但是出现了一个性质:
两个序列的子序列,一定是A的子序列。而A本身就是单调递增的。
因此这个子序列是单调递增的。换句话说,只要这个子序列在B中单调递增,它就是A的子序列。
哪个最长呢?当然是B的LIS最长。
自此完成转化。
#include<bits/stdc++.h> using namespace std; int a[100010], b[100010],f[100010],c[100010]; int main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); int n,len=0; cin >> n; for (int i = 1; i <=n; i++) { cin >> a[i]; f[a[i]] = i; } for (int i = 1; i <= n; i++)cin >> b[i]; for(int i=1;i<=n;i++){ if (f[b[i]] >=c[len]) { c[++len] = f[b[i]];//这里使用++len是保证上式的c[len]为数组中的最后一个元素; } else { *lower_bound(c+1, c + len+1, f[b[i]] )= f[b[i]]; } } cout <<len; return 0; }