最长公共子序列LCS
模板:
#include<iostream> #include<cstdio> #define maxn 10010 using namespace std; template<typename T> inline void read(T &x){ x=0; bool flag=0; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') flag=1; for(;isdigit(c);c=getchar()) x=x*10+(c^48); if(flag) x=-x; } int n; int a[maxn],b[maxn],f[maxn][maxn]; int main(){ read(n); for(int i=1;i<=n;i++) read(a[i]); for(int i=1;i<=n;i++) read(b[i]); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ f[i][j]=max(f[i-1][j],f[i][j-1]); if(a[i]==b[j]) f[i][j]=f[i-1][j-1]+1; } } printf("%d\n",f[n][n]); return 0; }
可惜暴力算法时间复杂度O(n2)被P1439【模板】最长公共子序列卡了50分
P1439【模板】最长公共子序列
所以正解是把LCS转换成求a的LIS得到时间复杂度O(nlogn)
注意这种做法要保证给出的是两个排列(每个数字只出现一次),否则映射的时候要每个数字映射多次,解决方法是把它倒过来映射,保证每个数字只取一次不会重复,好吧在确定每个数字能出现多少次(比如10次)的时候也能做,时间复杂度O(m*nlogn),m为每个数字出现次数(比如O(10*nlogn))
但是!对于普通的LCS来说,可能会有每个数字出现n次的极端情况!这样的复杂度就退化到了O(n2logn)!自己造数据的时候要注意!
贴一下AC代码:
#include<iostream> #include<cstdio> #include<algorithm> #define maxn 100010 using namespace std; template<typename T> inline void read(T &x){ x=0; bool flag=0; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') flag=1; for(;isdigit(c);c=getchar()) x=x*10+(c^48); if(flag) x=-x; } int n,a[maxn],b[maxn]; int cnt,rank[maxn],qianzhuihe[maxn];//rank存映射后的新队列,qianzhuihe存维护的LIS前缀 signed int main(){ read(n); for(int i=1;i<=n;i++) read(a[i]); for(int i=1;i<=n;i++) { read(b[i]); rank[b[i]]=i;//映射 } for(int i=1;i<=n;i++) a[i]=rank[a[i]]; for(int i=1;i<=n;i++){ if((cnt==0)||(a[i]>qianzhuihe[cnt-1])) qianzhuihe[cnt++]=a[i];//维护的前缀为空或满足递增条件时,把a[i]加到数组里 else { int s=lower_bound(qianzhuihe,qianzhuihe+cnt,a[i])-qianzhuihe;//找到前缀和(qianzhuihe)里第一个>a[i]的元素 ,注意s为下标 qianzhuihe[s]=a[i];//用a[i]替换掉这个元素 // * lower_bound(qianzhuihe,qianzhuihe+cnt,a[i])=a[i];//另一种写法,与上面两行等价 // 注意,上升为>=a[i]第一个元素 用lower_bound,不降为>a[i]第一个元素 用upper_bound } } printf("%d\n",cnt); return 0; }
注意第26行是 qianzhuihe[cnt++] 而不是 qianzhuihe[++cnt]并在输出时cnt-1 ,否则得分100->30