从给定序列中按原来的顺序取出若干元素组成的序列称为子序列,当取出全部元素时,子序列等于原序列

最长公共子序列:

给定两个序列,求序列最长的公共子序列,

如a=[1,2,3,4,5,6] b=[4,5,6,1,2,4]对于a b来说,最长公共子序列长度为3,有两个解[4,5,6][1,2,4]

可以设二维数组dp,横轴表示b纵轴表示a,即dp[1]表示到4为止的最长公共子序列,dp[2]表示[4,5]的最长公共子序列,以此类推

迭代下去,对于a来说,则是每次读入一个值比较,比如读入1,则显然dp[1][1]=dp[1][2]=dp[1][3]=0   dp[1][4]=dp[1][5]=dp[1][6]=1

通过不断读入a的值从左到右更新dp数组,最后dp[m][n]即为所求的最长公共子序列

当a[i]==b[j]时,可以从dp[i-1][j-1]推出dp[i][j],因为多匹配了上了一个所以dp[i][j]=dp[i-1][j-1]+1

当不相等时,dp[i][j]=max(dp[i-1][j],dp[i][j-1])

匹配不上,所以应该从对于a中1到 i 的元素、b中 1 到(j-1)的最长公共子序列dp[ i ][ j-1 ]与

a中1到(i-1)的元素、b中1到 j  的最长公共子序列dp[ i-1][ j ]比较更新dp[ i ][ j ]的值

时间复杂度为O(m*n)

int i,j;
for(i=1;i<=m;i++)
    for(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]);
    }    

 

最长递增子序列:

给定一个序列,求最长递增子序列

对于a = [ 2 ,5 ,6, 3,4, 5]来说最长递增子序列即[2 ,3, 4, 5]

 1 int i,j;
 2 for(i=1;i<=m;i++)
 3 {
 4     dp[i]=1;
 5     for(j=1;j<i;j++)
 6     {
 7         if(a[i]>a[j])
 8             dp[i]=max(dp[i],dp[j]+1)
 9      }
10 }

最朴素的思想就是O(m^2)对于每次读入的a[ i ]值以a[ j ]( i < j)比较,若a[ i ]>a[ j ] 则dp[ i ]可以取dp[ j ]+1

但是不一定dp [ i ]就比dp [ j ]+1小,所以要做个选择,上例子中用a [4 ]=3更新dp数组时,显然dp[ 4]最大只能更新为2

而dp[ 3]此时已经为3(2,5,6)在之后若来个7,dp[ i ]先扫到dp[3]则会更新为4显然比扫到dp[ 4 ]时要大

不要忘了先将dp[ i ]置1,因为即使a[ i ]是最小的,最长递增子序列也至少为1而不是0

如果只关注最长递增子序列长度,也可以使用二分搜索来优化,达到O(nlogn)的时间复杂度

 1 const int inf=0x3f3f3f3f;
 2 int i,j,ans=0;
 3 memset(dp,inf,sizeof(inf));
 4 for(i=1;i<=n;i++)
 5 {
 6     j=lower_bound(dp+1,dp+1+n,a[i])-dp;
 7     dp[j]=a[i];
 8 }
 9 i=1;
10 while(dp[i]!=inf)
11     ans++,i++;
12 cout<<ans<<endl;

先把dp数组置为一个很大的数,每次读入a[i]都用lower_bound(二分,在[1,n+1)中找到第一个大于等于a[i]的值,返回该值的地址)更新dp数组

此时数组中非等于inf的元素个数即为最长递增子序列,例如对于

a=[2,5,6,3,4,5]

在读入a[3]时dp=[2,5,6,inf....]此时在读入a[4]  dp=[2,4,6,inf...]虽然在dp数组中4跑到6前面

但是由于我们只关心最长递增子序列长度,所以不影响

后续读到最后一个5时dp数组就会更新为dp=[2,3,4,5,inf,inf....]

若再来个1则变成dp=[1,3,4,5,inf,inf...]显然来个1后最长递增子序列长度依然为4

 

最长公共递增子序列:

给定两个序列,求最长公共递增子序列。求这个不能通过先求两个最长递增子序列,再求最长公共子序列的方法

因为最长公共递增子序列不一定在最长递增子序列里,例如

a=[,6,7,1,2,3,4]

b=[11,12,13,14,6,7]

 1 int i ,j;
 2 int tmp;
 3 for(i=1;i<=m;i++)
 4 {
 5     tmp=0;
 6      for(j=1;j<=n;j++)
 7      {
 8          if(a[i]>b[j])
 9              tmp=max(tmp,dp[j]);
10          else if(a[i]==b[j])
11              dp[j]=tmp+1;
12   }
13 }

 

dp[ i ]表示对于b数组1到 i 的最长递增子序列,遍历a中元素不断更新dp值

对于 i  > j 若有 a[ i ]==b[ i ]  那么dp[ i ]=tmp+1

tmp=max(dp [ j ]) 

posted on 2020-12-04 20:35  丶蛋花拉面  阅读(181)  评论(0)    收藏  举报