动态规划:找出所有的LCS

动态规划:找出所有的LCS

用动态规划法求解问题特性:a.具有重叠性;b.具有最优子结构性质

1.最长公共子序列(LCS)的概念:

若Z<X,Z<Y,且不存在比Z更长的X和Y 的公共子序列,

则称Z是X和Y 的最长公共子序列,记为ZÎLCS(X , Y)。

最长公共子序列往往不止一个。

e.g. X=<A,B,C,B,D,A,B>,  Y=<B,D,C,A,B,A>, 则

Z=<B,C,B,A>,   Z’=<B,C,A,B>,  Z’’=<B,D,A,B>

均属于LCS(X , Y) ,即X,Y有3个LCS。

2.寻找最长公共子序列

由书上分析结果

  (1)若xm=yn,则问题化归成求Xm-1与Yn-1的LCS

  (LCS(X , Y)的长度等于LCS(Xm-1 , Yn-1)的长度加1)

  2若xm≠yn 则问题化归成求Xm-1与Y的LCS及X与Yn-1的LCS

  LCS(X , Y)的长度为:

max{LCS(Xm-1 , Y)的长度, LCS(X , Yn-1)的长度}

3.求取最长公共子序列

  引进一个二维数组C,用C[i,j]记录Xi与Yj的LCS的长度

  如果我们是自底向上进行递推计算,那么在计算C[i,j]之前,

C[i-1,j-1], C[i-1,j]与C[i,j-1]均已计算出来。此时我们

根据X[i]=Y[j]还是X[i]¹Y[j],就可以计算出C[i,j]:

若X[i]=Y[j],则执行C[i,j]←C[i-1,j-1]+1;若X[i]¹Y[j],则根据:

C[i-1,j]≥C[i,j-1],则C[i,j]取C[i-1,j];否则C[i,j]取C[i,j-1]。即有

C[i,j]=

e.g. 如下图:

  

   0     1     2     3    4    5     6

      y    B      D     C    A    B    A

0  xi  0     0     0    0     0    0     0

              ←↑     ←↑         

1  A   0    0  0     1  1     1

                            

2  B   0    1   1       2   2

                 ←↑          ←↑   ←↑

3  C   0    1      2   ←2    2

            ↖     ←↑     ←↑   

4  B   0    1   1         3   

                     ←↑  ←↑     ←↑

5  D   0    1     2    2      3

                     ←↑    ←↑  

6  A   0    1     2  2     3  3     4

                     ←↑         ←↑

7  B  0     1     2  2     3    4   4

为了搜索到所有的LCS的,使用一个m´n的二维数组b,

b[i,j]记录C[i,j]是通过哪一个子问题的值求得的,

以决定搜索的方向:

若C[i-1,j]>C[i,j-1],则b[i,j]中记入“↑”;

C[i-1,j]=C[i,j-1]时,b[i,j]中记入“←↑”,

若C[i-1,j]< C[i,j-1],则b[i,j]中记入“←”;

执行的搜索方向:

X[i]=Y[j]要执行b[i,j]=“↖”,

X[i]¹Y[j]且C[i,j-1] > C[i-1,j]要执行b[i,j]=“←”,

X[i]¹Y[j]且C[i,j-1] < C[i-1,j] 要执行b[i,j]=“↑”,

X[i]¹Y[j]且C[i,j-1] = C[i-1,j] 要执行b[i,j]=“←↑”,

算法 LCS_L(X,Y,m,n,C)

for i=0 to m do C[i,0]←0

for j=1 to n do C[0,j]←0

for i=1 to m do {

   for j=1 to n do{

     if x[i]=y[j] then {C[i,j]←C[i-1,j-1]+1;

                   b[i,j]←“↖” ;}

}else if C[i-1,j]>C[i,j-1] 

then {

C[i,j]←C[i-1,j];

        b[i,j]←“↑” ;

 }

else if C[i-1,j]=C[i,j-1] 

then {

C[i,j]←C[i-1,j];

        b[i,j]←“←↑” ;

 }

  }else{C[i,j]←C[i,j-1];

       b[i,j]←“←” ;} 

算法的时间复杂度显然是Q(m×n)的。

4 程序如下:

// 输出LCS.cpp 

#include<windows.h>

#include<time.h>

#include<string.h>

#include<stdlib.h>

#include<stdio.h>

#include<iostream>

using namespace std;

//*******************************寻找最长公共子序列***********************//

void  LCS_L(int X[],int Y[],int m,int n,int C[][10],char D[][10])

{

int i,j,k,l;

for(i=0;i<=m;i++) //给数组C、D边界赋值

{

C[i][0]=0;

D[i][0]='0';

} //i=0时取值为0

for(j=0;j<=n;j++)

{

C[0][j]=0;

D[0][j]='0';

}  //j=0时取值为0

for(i=1;i<=m;i++)                          //获取C[i][j]的值

{

for(j=1;j<=n;j++)

{

if(X[i-1]==Y[j-1])     //两数组的元素相同值相等

{

C[i][j]=C[i-1][j-1]+1;//

D[i][j]='$';

}

else

{

if(C[i-1][j]>C[i][j-1])//比较大小求取C、D两数组的对应值

{

C[i][j]=C[i-1][j];

D[i][j]='>';

}

else if(C[i-1][j]==C[i][j-1])

{

C[i][j]=C[i-1][j];

D[i][j]='=';

}

else

{

C[i][j]=C[i][j-1];

D[i][j]='<';

}

}

}

}

cout<<endl<<"输出二维数组b"<<endl;

for(k=0;k<=10;k++)     //输出二维数组b

{

for(l=0;l<=9;l++)

cout<<D[k][l]<<"  ";

cout<<endl;

}

//调用显示最长公共子序列函数

}

//***********************输出一个最长公共子序列**********************//

void LCS_OutputOne(int C[][10],int X[],int Y[],int i,int j)

{

if(i==0||j==0)

{

cout<<endl<<"An LCSLength string: ";

return;

}  //到边界是返回

    if(X[i-1]==Y[j-1])            //b[i][j]=='$'

{

LCS_OutputOne(C,X,Y,i-1,j-1);   //对角线查找

cout<<X[i-1];

}

else

if(C[i-1][j]>C[i][j-1])

LCS_OutputOne(C,X,Y,i-1,j);   // b[i][j]=='>'

else  if(C[i-1][j]<C[i][j-1])

LCS_OutputOne(C,X,Y,i,j-1); //b[i][j]='<'

else

LCS_OutputOne(C,X,Y,i,j-1);      //b[i][j]='='

}

//***********************输出所有最长公共子序列**********************//

void LCS_OutputAll(int C[][10],char D[][10],int X[],int i,int j,int Result[],int len,int k)

{

if(i==0 || j==0)//到边界时返回

{

int n;

for(n=0;n<k;n++)

{

    cout<<Result[n]; //输出数值

            }

            cout<<endl;

return;

}

else

{if(C[i][j]==C[i-1][j-1]+1)//左上走

{

Result[len-1]=X[i-1];//取数组值

len--;

LCS_OutputAll(C,D,X,i-1,j-1,Result,len,k);

len++;

}

else

{

if(D[i][j]=='>')//向上走

{

LCS_OutputAll(C,D,X,i,j-1,Result,len,k);

}

else if(D[i][j]=='<')//向左走

{

LCS_OutputAll(C,D,X,i-1,j,Result,len,k);

}

else

{

LCS_OutputAll(C,D,X,i,j-1,Result,len,k);

LCS_OutputAll(C,D,X,i-1,j,Result,len,k);

}

}

}

}

//***************************主函数********************************//

int main()

{

int i,j,k,x,y;

    int len=0;

int A[10]={0};

int B[9]={0};

int C[11][10]={0};

char D[11][10]={0};

system("color FC");

srand(time(0));

cout<<"数组A:";

for(i=0;i<10;i++)//

{

A[i]=rand()%3;

cout<<A[i]<<" ";

}

cout<<endl<<"数组B:";

for(j=0;j<9;j++)//

{

B[j]=rand()%3;

cout<<B[j]<<" ";

}

LCS_L(A,B,10,9,C,D);//

cout<<"显示二维数组C"<<endl;

for(i=0;i<11;i++)      //显示二维数组C

{

for(j=0;j<10;j++)

cout<<C[i][j]<<"  ";

cout<<endl;

}

len=C[i-1][j-1];//

x=10;

y=9;

k=len;

int Result[len];

cout<<endl<<"lcs长度为:"<<len;

LCS_OutputOne(C,A,B,x,y);

cout<<endl<<endl;

cout<<"所有的Lcs如下:"<<endl;

LCS_OutputAll(C,D,A,x,y,Result,len,k);

cout<<endl<<"结束";

return 0;

}

5.结果分析:

由LCS_Output_All函数可知,求出所有的LCS的长度就是根据数组b[i][j]中的值,即搜索的方向信息来遍历所有可能的路径找到满足条件的元素集合。函数LCS_L的时间复杂度是求解数组b和c的执行时间,是O(mn+m+n)。而函数LCS_Output_All的时间复杂度取决于遍历的路径数。在最好的情况下,即X序列和Y序列完全相等,m=n,数组b中的值都是$(指向对角线方向),所以时间复杂度是O(m)。而在最坏情况下,即X序列和Y序列不存在公共子序列,数组b中的值都是“4”,就是要分别沿向上和向左的方向搜索,这是每处理一次X[i]和Y[j],都必须沿着两个方向调用函数LCS_Output_All,当遇到i=0或j=0的情况开始返回,只有在搜索完所有的路径后算法才结束。要确定最坏情况的时间复杂度,就是要计算出从点(m,n)到i=0或j=0(除(i,j)=(0,0)外)的所有路径。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值