子序列问题学习指南
前置芝士
最长公共子序列
朴素算法
时间复杂度:\(O(n^2)\)
const int N=1001;
int a[N],b[N],dp[N][N];
int n;
int 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]){
dp[i][j]=dp[i-1][j-1]+1;
}else{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
}
cout<<dp[n][n]<<endl;
}
优化算法
时间复杂度:\(O(nlogn)\)
离散化+最长递增子序列+二分查找
const int N=100007;
int a[N],b[N],f[N],t[N];
int n;
void solve(){
cin>>n;
for(int i=1;i<=n;i++) {cin>>a[i];t[a[i]]=i;}
for(int i=1;i<=n;i++) {cin>>b[i];f[i]=0x3f3f3f3f;}
f[t[b[1]]]=1;
int len=1;
for(int i=2;i<=n;i++){
if(t[b[i]]>f[len]) f[++len]=t[b[i]];
else{
int l=1,r=len,mid;
while(l<r){
mid=(l+r)/2;
if(f[mid]>t[b[i]]) r=mid;
else l=mid+1;
}
f[l]=min(f[l],t[b[i]]);
}
}
cout<<len<<endl;
}
最长上升子序列
朴素算法
时间复杂度:\(O(n^2)\)
优化算法
时间复杂度:O(nlogn)
如果当前有两个长度为2的上升子序列[1,2]和[1,4],那么显而易见,前者要更优。因为前者的末位元素2要比后者的末位元素4更小。如果后续遍历到的新元素为3,则就可以加入前者,而不能加入后者。
所以,我们总是希望当前构造的上升子序列中的元素较小。