luoguP3402 最长公共子序列(LCS-->LIS)
2017-08-09 15:45
176 查看
题目背景
DJL为了避免成为一只咸鱼,来找Johann学习怎么求最长公共子序列。
题目描述
经过长时间的摸索和练习,DJL终于学会了怎么求LCS。Johann感觉DJL孺子可教,就给他布置了一个课后作业:
给定两个长度分别为n和m的序列,序列中的每个元素都是正整数。保证每个序列中的各个元素互不相同。求这两个序列的最长公共子序列的长度。
DJL最讨厌重复劳动,所以不想做那些做过的题。于是他找你来帮他做作业。
输入输出格式
输入格式:
第一行两个整数n和m,表示两个数列的长度。
第二行一行n个整数a_1,a_2,…,a_n,保证1≤a_i≤〖10〗^9。
第三行一行m个整数b_1,b_2,…,b_m,保证1≤b_i≤〖10〗^9。
输出格式:
一行一个整数,表示两个数列的最长公共子序列的长度。
输入输出样例
输入样例#1:
6 6
1 3 5 7 9 8
3 4 5 6 7 8
输出样例#1:
4
分析:
一眼秒N^2dp
然而面对300000就jj了,
这样我们就要学习新知识了:
显然dp啊:
一、n^2算法
f[i][j]表示a串前i个和b串前j个的最长公共子串的长度
若A[i]==B[j],f[i][j]=f[i-1][j-1]+1
否则f[i][j]=max(f[i-1][j],f[i][j-1])
二、nlogn算法
f[i][j]仅在A[i]==B[j]处才增加,
对于不相等的地方对最终值是没有影响的
故枚举相等点处可以对其进行优化
则对于f[i][j](这里只计算a[i]==b[j]的i和j),
取最大的f[p][q],满足(p < i,q < j),通过二叉搜索树可以在logn的时间里获取到最大的f[p][q])。
A:abdba
B:dbaaba
1:先顺序扫描A串,取其在B串的所有位置:
2:a(2,3,5) b(1,4) d(0)
3:A用每个字母的反序列替换,则最终的最长严格递增子序列的长度即为解
最长递增子序列可在O(nlogn)的时间内算出
替换结果:532 41 0 41 532
对于序列53241041532做LIS
最大长度为3.
这样的g是单调增的,
每次考虑以i为结尾的上升子序列时,我们只要在g二分中找到小于a[i]的最大值g[j]
f[i]=j+1
然而在真正写的时候f数组就可以省去了
回到LCS
简单说明:上面的序列和最长公共子串是等价的
对于一个满足最长严格递增子序列的序列,该序列必对应一个匹配的子串
a串中的每一个数子都是该字符在b中出现的位置
反序是为了在递增子串中,每个字母对应的序列最多只有一个被选出
反证法可知不存在更大的公共子串,因为如果存在,则求得的最长递增子序列不是最长的,矛盾。
不太明白。。。
ps:LCS(最长公共子序列)在最终的时间复杂度上不是严格的O(nlogn),不知均摊上是不是。
举个退化的例子:
A:aaa
B:aaaa
则序列=>321032103210
长度变成了n*m ,最终时间复杂度O(n*m*(lognm)) > O(n*m)。
这种情况不知有没有很好的解决办法
这样就可以把A转化成一个n级别的序列,不然的话时间会退化得很厉害
又因为ai<=1e9,这样就需要一个map映射一下,记录每个数对应的位置,当然还有hash做法
DJL为了避免成为一只咸鱼,来找Johann学习怎么求最长公共子序列。
题目描述
经过长时间的摸索和练习,DJL终于学会了怎么求LCS。Johann感觉DJL孺子可教,就给他布置了一个课后作业:
给定两个长度分别为n和m的序列,序列中的每个元素都是正整数。保证每个序列中的各个元素互不相同。求这两个序列的最长公共子序列的长度。
DJL最讨厌重复劳动,所以不想做那些做过的题。于是他找你来帮他做作业。
输入输出格式
输入格式:
第一行两个整数n和m,表示两个数列的长度。
第二行一行n个整数a_1,a_2,…,a_n,保证1≤a_i≤〖10〗^9。
第三行一行m个整数b_1,b_2,…,b_m,保证1≤b_i≤〖10〗^9。
输出格式:
一行一个整数,表示两个数列的最长公共子序列的长度。
输入输出样例
输入样例#1:
6 6
1 3 5 7 9 8
3 4 5 6 7 8
输出样例#1:
4
分析:
一眼秒N^2dp
然而面对300000就jj了,
这样我们就要学习新知识了:
最长公共子序列问题(LCS):
给定2个字符串,求其最长公共子串。如abcde和dbada的最长公共字串为bd。显然dp啊:
一、n^2算法
f[i][j]表示a串前i个和b串前j个的最长公共子串的长度
若A[i]==B[j],f[i][j]=f[i-1][j-1]+1
否则f[i][j]=max(f[i-1][j],f[i][j-1])
二、nlogn算法
f[i][j]仅在A[i]==B[j]处才增加,
对于不相等的地方对最终值是没有影响的
故枚举相等点处可以对其进行优化
则对于f[i][j](这里只计算a[i]==b[j]的i和j),
取最大的f[p][q],满足(p < i,q < j),通过二叉搜索树可以在logn的时间里获取到最大的f[p][q])。
这里也可将其转化为最长递增子序列问题
举例说明:A:abdba
B:dbaaba
1:先顺序扫描A串,取其在B串的所有位置:
2:a(2,3,5) b(1,4) d(0)
3:A用每个字母的反序列替换,则最终的最长严格递增子序列的长度即为解
最长递增子序列可在O(nlogn)的时间内算出
替换结果:532 41 0 41 532
对于序列53241041532做LIS
最大长度为3.
LIS(最长上升子序列nlogn)
建一个g数组,g[i]表示长度为i的lis中,作为结尾的最小的数,这样的g是单调增的,
每次考虑以i为结尾的上升子序列时,我们只要在g二分中找到小于a[i]的最大值g[j]
f[i]=j+1
然而在真正写的时候f数组就可以省去了
g[1]=num[1]; //长度为1的最小 //f[1]=1; 写完会发现f可有可无 int t=1; //当前最长上升子序列的长度 for (i=2;i<=n;i++) { int l=1,r=t,mid; while (l<=r) { mid=(l+r)>>1; if (g[mid]<num[i]) //找到小于a[i]的最大的一个 l=mid+1; //mid+1 不知不觉就+1 else r=mid-1; } g[l]=num[i]; //l就是长度 //f[i]=l; if (l>t) t=l; } return t; //最长上升子序列长度
回到LCS
简单说明:上面的序列和最长公共子串是等价的
对于一个满足最长严格递增子序列的序列,该序列必对应一个匹配的子串
a串中的每一个数子都是该字符在b中出现的位置
反序是为了在递增子串中,每个字母对应的序列最多只有一个被选出
反证法可知不存在更大的公共子串,因为如果存在,则求得的最长递增子序列不是最长的,矛盾。
不太明白。。。
ps:LCS(最长公共子序列)在最终的时间复杂度上不是严格的O(nlogn),不知均摊上是不是。
举个退化的例子:
A:aaa
B:aaaa
则序列=>321032103210
长度变成了n*m ,最终时间复杂度O(n*m*(lognm)) > O(n*m)。
这种情况不知有没有很好的解决办法
然而
这道题保证每个序列中的各个元素互不相同这样就可以把A转化成一个n级别的序列,不然的话时间会退化得很厉害
又因为ai<=1e9,这样就需要一个map映射一下,记录每个数对应的位置,当然还有hash做法
//map-> #include<cstdio> #include<iostream> #include<cstring> #include<map> using namespace std; const int N=300005; int n,m,a ,g ,num ; map<int,int> mp; /*void doit() //n^2 { int i,j,ans=0; for (i=1;i<=n;i++) for (j=1;j<=m;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]); } printf("%d",f [m]); }*/ int LCS() { int i,j; int s=1; while (!num[s]) num[s]=999999,s++; //如果a[s]==0表示b中没有这个数 g[1]=num[1]; //长度为1的最小 //f[1]=1; 写完会发现f可有可无 int t=1; //当前最长上升子序列的长度 for (i=2;i<=n;i++) { int l=1,r=t,mid; while (l<=r) { mid=(l+r)>>1; if (g[mid]<num[i]) //找到小于a[i]的最大的一个 l=mid+1; //mid+1 不知不觉就+1 else r=mid-1; } g[l]=num[i]; //l就是长度 //f[i]=l; if (l>t) t=l; } return t; } void doit() { int i,j,cnt=0; for (i=1;i<=n;i++) num[i]=mp[a[i]]; //a中的数字在b中的位置 printf("%d",LCS()); } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=m;i++) { int u; scanf("%d",&u); mp[u]=i; //数字u在b中出现的位置 } doit(); return 0; }
相关文章推荐
- bzoj 4990(LCS->LIS)
- LCS(最长公共子序列)、LIS(最长上升子序列)、LCIS(最长公共上升子序列)
- 最长上升子序列问题(LIS)和最长公共子序列问题(LCS)
- 【LIS和LCS】最长上升子序列和最长公共子序列
- LIS&LCS最长递增子序列和最长公共子序列问题
- 最长公共子序列(LCS)、最长递增子序列(LIS)、最长递增公共子序列(LICS)
- 最长上升子序列(LIS)&最长公共子序列(LCS)
- Algorithm --> 最长公共子序列(LCS)
- 动态规划——最长公共子序列(LCS)&最长递增子序列(LIS)
- uva 10635 - Prince and Princess lcs->lis 2018-2-8
- 【动态规划基础篇】【矩阵取数/最大字段和/最长公共子序列(LCS)/编辑距离/最长上升子序列(LIS)】
- [luoguP3402] 最长公共子序列(DP + 离散化 + 树状数组)
- LIS(最长递增子序列)和LCS(最长公共子序列)的总结
- 算法系列笔记6(动态规划—最长公共子序列/串lcs和最长递增子序列(LIS))
- 最长上升子序列(LIS) -最长公共子序列(LCS)
- 单调递增最长子序列(LIS)&&最长公共子序列(LCS)
- 最长公共子序列(LCS)and最长上升子序列(LIS)
- 最长上升子序列(LIS)与最长公共子序列(LCS)
- 一中OJ #1434 序列的LCS [UVa 10635 -> Prince and Princess] | 动态规划 LCS映射转换LIS | 解题报告
- 【LCS,LIS】最长公共子序列、单调递增最长子序列