DP最长公共子序列

来源:互联网 发布:滨州行知中学招聘 编辑:程序博客网 时间:2024/05/22 03:41

问题描述:

给定两个序列X={x1,x2,x3,.....,xn}和Y={y1,y2,y3,....,yn},找出X和Y的最长公共子序列。

问题分析:

可以使用穷举搜索算法。对X的所有子序列,检查它是否也是Y的子序列,从而确定它是否为X 和Y的公共子序列。并且在检查过程中记录最长得公共子序列。X的所有子序列都检查过后即可求出X和Y的最长公共子序列。但是,穷举算法需要指数时间。所以使用动态规划的算法,自底向上解决该问题。

1.最长公共子序列的结构

 具有最优子结构性质。

2.子问题的递归结构

           |------ 0                                i=0,j=0

c[i][j]=|------c[i-1][j-1]+1                 i,j>0;xi=xj

         |------max{c[i][j-1],c[i-1][j]}    i,j>0;xi!=xj

3.计算最优值

public static int lcsLength(char[]x,char[y],int[][]b)
{
int m=x.length-1;
int n=y.length-1;
int[][]c=new int[m+1][n+1]; //c[i][j]记录Xi和Yj的最长公共子序列的长度
    for(int i=1;i<=m;i++)c[i][0]=0;
for(int i=1;i<=n;i++)c[0][i]=0;//构造子问题的解


    for(int i=1;i<=m;i++)          //由子问题的解逐步向上求出原问题的解
for(int j=1;j<=n;j++)
   {
  if(x[i]==y[j])
{
  c[i][j]=c[i-1][j-1]+1;
  b[i][j]=1;                    //b[i][j]的三个取值分别对应三种不同的情况
  }
  else if(c[i-1][j]>=c[i][j-1])
{
  c[i][j]=c[i-1][j];
  b[i][j]=2;
  }
  else{}
  c[i][j]=c[i][j-1];
  b[i][j]=3;
   }
return c[m][n];
}

在写上述代码时,先将最小子问题写出,然后逐步写出次小子问题,然后观察规律,写出代码,实例如下:

c[1][0] c[2][0] c[3][0] c[4][0] c[5][0]....
c[0][1] c[0][2] c[0][3] c[0][4] c[0][5]....
c[1][1]=(X1==Y1)?(c[0][0]+1:(max(c[1][0],c[0][1],
c[1][2]=(X1==Y2)?(c[0][1]+1:(max(c[1][1],c[0][2])//可以看出由已知子问题的解求出未知规模更大子问题的解
.......
c[2][1]=(X2==Y1)?(c[1][0]+1:(max(c[1][1],c[2][0])
.......
c[i][j]=(Xi=Yj)?(c[i-1][j-1]+1:(max(c[i-1][j],c[i][j-1])
.......
c[m][n]=········

4.构造最长公共子序列

 在3中由数组b[i][j]记录了由哪个子问题的解得到的,共有三个子问题(Xi=Yj,c[i-1][j]>=c[i][j-1],c[i-1][j]<=c[i][j-1]),分别对应三个不同的数值1,2,3;因此只要从b[m][n]开始,只要b[i][j]=1,则输出x[i]或者是y[j];若不等于1,则继续向下搜索,直到i或者j等于0;

代码如下:

public static void lcs(int i,int j,char[]x,int[][]b)
{
if(i==0||j==0)return;
if(b[i][j]==1){
lcs(i-1,j-1,x,b);
System.out.print(x[i]);
}
else if(b[i][j]==2)lcs(i-1,j,x,b);
else lsc(i,j-1,x,b);
    
}

总结:

1.遇到该类问题可以查看已解决的相似问题,以便构造c[i][j]以及b[i][j]这些记录问题的条件。

2.先由最小子问题向外推,逐步得到更大子问题的解。建立递归结构后,方便由递归结构转换成代码。

3.递归算法,要注意理解问题的本质以及递归的结束条件。


1 0
原创粉丝点击