题目描述
从汉中回来后,pear编制了一本教义问答手册。为了防止别人盗窃这本价值连城的手册,pear决定与bx2k一起编密码来将手册加密。
pear的编制密码方法是这样的,他先让他自己和bx2k选各一个1-n的排列,然后pear会将这两个排列的最长公共子序列作为密码。bx2k想知道这个密码的长度,又不想告诉你密码,于是他就把这两个排列做了适当改动后来问你。
输入输出格式
输入格式
第一行包含一个数n。
第二行包含一个1-n的排列,代表适当改动后的bx2k的排列。
第三行包含一个1-n的排列,代表适当改动后的pear的排列。
输出格式
一行,第一行包含一个数ans,代表这两个排列的最长公共子序列的长度。
输入输出样例
输入样例
5
4 5 1 2 3
1 4 2 5 3
输出样例
3
说明
样例说明
{1,2,3}为一个最长公共子序列,长度为3。
数据规模
对于30%的数据,n≤5;
对于70%的数据,n≤1000;
对于100%的数据,n≤10000。
题解
直接用dp求LCS的时间复杂度很大,为O(n^2),我们要换一种做法。
因为这两个序列的元素是相同的,我们可以用一个pos数组储存a[i]在b序列的位置。
由于公共子序列是要求位置保持升序的,我们就可已转换成求pos数组的LIS了。
现在就可以用树状数组等数据结构来优化dp的,但是下面我要介绍一种贪心做法(应该是)。
此时开一个st数组来装LIS。
根据贪心,我们可以得到:st前面尽可能小,st后面就能尽可能进入新的元素,我们只需要不断更新已有的LIS就行了。
因为st满足单调性,所以用二分优化就可以把时间复杂度降到O(nlogn)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #define MAX_N 100000 using namespace std; int n; int a[MAX_N + 5]; int b[MAX_N + 5]; int pos[MAX_N + 5]; int s[MAX_N + 5], cnt; int main() { scanf("%d", &n); for(register int i = 1; i <= n; ++i) { scanf("%d", a + i); } for(register int i = 1; i <= n; ++i) { scanf("%d", b + i); } for(register int i = 1; i <= n; ++i) { pos[b[i]] = i; } int lt, rt, mid; for(register int i = 1; i <= n; ++i) { if(pos[a[i]] > s[cnt]) { s[++cnt] = pos[a[i]]; continue; } lt = 1, rt = cnt; while(lt < rt) { mid = lt + rt >> 1; if(pos[a[i]] < s[mid]) rt = mid; else lt = mid + 1; } s[rt] = pos[a[i]]; } printf("%d", cnt); return 0; }