转载:最长公共子序列问题

来源:互联网 发布:求积分软件 编辑:程序博客网 时间:2024/06/06 04:39

/*----------------------------文件头--------------------------------
  作者:lovefox
  Email:lbfjfz@tom.com
  作业名称:最长公共子序列问题

-------------------------------------------------------------------*/

#include
#include
#include


//--------------------- 变量、函数定义 开始 ------------------

ofstream myoutf("output.txt");            //输出到文件,全局变量


/*------------------ LCSLength函数定义 -----------------------
 *  函 数 名: LCSLength                                      *
 *  返回类型: void 无返回值                                  *
 *  参数说明: m   X序列长度                                  *
 *            n   Y序列长度                                  *
 *     x   序列X                                      *
 *     y   序列Y                                      *
 *     c   最长公共子序列长度                         *
 *     b   记录c的值是由哪一个子问题的解得到的        *
 *  功    能: 输出 n个矩阵连乘的最优值                       *
 *  调用示例: LCSLength(m,n,x,y,c,b)                         *
 *-----------------------------------------------------------*/

void LCSLength(int m,int n,char * x,char * y,int * * c,int * * b);

/*--------------------- LCS函数定义 --------------------------
 *  函 数 名: LCS                                            *
 *  返回类型: void 无返回值                                  *
 *  参数说明: m   X序列长度                                  *
 *            n   Y序列长度                                  *
 *     x   序列X                                      *
 *     y   序列Y                                      *
 *     c   最长公共子序列长度                         *
 *     b   记录c的值是由哪一个子问题的解得到的        *
 *  功    能: 输出 n个矩阵连乘的最优值                       *
 *  调用示例: LCSLength(m,n,x,y,c,b)                         *
 *-----------------------------------------------------------*/

void LCS(int t,int j,char * x,int * * b);

//--------------------- 变量、函数定义 结束 ------------------

 

//--------------------- 主函数 开始 --------------------------
void main()
{
  int m , n;                                  //定义连乘的矩阵数
  int i , j;                                  //循环变量
  char * x , * y;                             //序列x和y
  int * * c;                                  //最长公共子序列长度
  int * * b;

  ifstream myinf("input.txt",ios::nocreate);  //读取文件
  if (myinf.fail())
  {
   cerr << "读入文件时,出错!";
   return;                                   //如果没有输入文件,则返回错误
   }//if (myinf.fail())
 
  myinf >> m >> n;                            //读取矩阵个数
  m = m + 1;
  n = n + 1;
 
  //----------- 读入字符到序列x中 --------------
  x = new char[m];
  for (i=1;i  {
    myinf >> x[i];
 cout << x[i];
  }//for
  //----------- 读入字符到序列y中 --------------
  cout << endl;

  y = new char[n];
  for (i=1;i  {
    myinf >> y[i];
 cout << y[i];
  }
  cout << endl;

  c = new int * [m];
  for (i=0;i

  for (i=0;i  {  for (j=0;j   {
     c[i][j] = 0;
  cout << "i=" << i << ",j=" << j << " ";
   }
     cout << endl;
  }

  b = new int * [m];
  for (i=0;i

  for (i=0;i   for (j=0;j   {
     b[i][j] = 0;
   }

  //-------------------------------------

  LCSLength(m-1,n-1,x,y,c,b);
  LCS(m-1,n-1,x,b);

  //-------------- 释放数组 -------------
 
  for (i=0;i  delete[] b; 

  for (i=0;i  delete[] c;

  delete[] x; 
  delete[] y;
 
  myinf.close();                              //关闭输入文件
  myoutf.close();                             //关闭输出文件
}//void main()

//--------------------- 主函数 结束 --------------------------


/*------------------ LCSLength函数定义 -----------------------
  函 数 名: LCSLength
  返回类型: void 无返回值
  参数说明:
  功    能:
  调用示例:


-------------------------------------------------------------*/

void LCSLength(int m,int n,char * x,char * y,int * * c,int * * b)
{
  int i,j;
  for (i=1;i<=m;i++) c[i][0] = 0;
  for (i=1;i<=n;i++) c[0][i] = 0;
   
 for(i=1;i<=m;i++)
   for (j=1;j<=n;j++)
   {
  if(x[i] == y[j])
  {
    c[i][j] = c[i-1][j-1] + 1;
    b[i][j] = 1;
  }//if x[i] == y[j]  
   
  else if(c[i-1][j] >= c[i][j-1])
  {
    c[i][j] = c[i-1][j];
    b[i][j] = 2;
  }//else if(c[i-1][j] >= c[i][j-1])

  else
  {
    c[i][j] = c[i][j-1];
    b[i][j] = 3;
  }//else

   }//for i j
 
  myoutf << c[m][n] <}//void LCSLength

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);
 cout << x[i];
 myoutf << x[i];
  }//if
  else if (b[i][j] == 2) LCS(i-1,j,x,b);
  else LCS(i,j-1,x,b);
 
}

转载于http://borland.mblogger.cn/lovefox/posts/18440.aspx


tag:字符串 序列 公共 子序列 c语言 编程 动态规划法 递归 软件设计师
text:把自己注释好的贴上来。没什么好说的,请看程序.本程序在linux+gvim+gcc平台下调试通过,在MINGW下应该也没什么问题。
copyright:归《软件设计师》作者所有,本文仅为学习,转载请全文转载。

总共2个程序,请注意分割线。

/*这个程序使用动态规划方法,求出两个字符串的最长公共序列,这里的公共序列于平常所想的不太一样,例如:BCDB就是ABCBDAB的一个子序列*/
/*
源程序出自《软件设计师》第二版page520*/
/*
经过初步调试.可以运行并得出正确结果*/
# include <stdio.h>
# include <string.h>

#define N 100/*
字符串的最大长度限制*/

char a[ N ],/*
第一个字符串*/
     b[ N ],/*
第二个字符串*/
     str[ N ];/*
用于存放a b的公共子序列*/

/*
计算两个序列的最长公共子序列的长度*/
int lcs_len( char *a, char *b, int c[][ N ] ){
    int m = strlen( a ),/*
计算两个序列的长度*/
        n = strlen( b ),
        i,/*
行指针*/
            j;/*
列指针*/
   
    for( i = 0; i <= m; i++ ) c[ i ][ 0 ] = 0;/*j==0
表示b串长度为0,无论a串多长,两者的公共子序列长度为0*/
    for( j = 1; j <= n; j++ ) c[ 0 ][ j ] = 0;/*
根据上一个类推*/
    for( i = 1; i <= m; i++ )/*
开始递推*/
        for( j = 1; j <= n; j++ )/*
若两序列的最后一个字符相同,那么公共子序列长度为去掉这最后一个字符后新生成的两个串的公共子序列长度+1*/
            if( a[ i - 1 ] == b[ j - 1 ]) c[ i ][ j ] = c[ i - 1 ][ j - 1 ] + 1;/**/
            else if( c[ i - 1 ][ j ] >= c[ i ][ j -1 ] ) c[ i ][ j ] = c[ i - 1 ][ j ];/*
若两序列最后一个字符不同,那么他们最大子序列的长度为c[ i - 1][j]c[i][j-1]中较大的一个*/
                 else c[ i ][ j ] = c[ i ][ j -1 ];

    return c[ m ][ n ];/*
结果:ab的最大子序列长度存在c[m][n]*/
}

/*
构造最长子序列函数*/
char *build_lcs( char s[], char *a, char *b ){/*
根据lcs_len()函数计算的结果,倒推出最长子序列,结果放在s[]*/
    int k,/*a
b最长子序列的长度*/
        i = strlen( a ),/*a
串长度*/
        j = strlen( b ),/*b*/
        c[ N ][ N ];/*
存放全部的计算过程,动态规划,体现在这个地方,中间计算结果都在这里*/
    k = lcs_len( a, b, c );/*
c[][]传给lcs_len()计算并求出长度,将中间结果放在c[][]*/
    s[ k ] = '/0';/*s
串的结束标记*/
    while( k > 0 )/*
开始倒推*/
        if( c[ i ][ j ] == c[ i - 1 ][ j ] ) i --;
        else if( c[ i ][ j ] == c[ i ][ j -1 ]) j--;
                else
                {    s[ --k ] = a[ i - 1 ];/*
将一个公共字符存入s*/
                     i--;
                     j--;
                }
    return s;
}

/*
测试程序*/
void main(){
    printf("Enter two string(<%d)!/n", N );
    scanf( "%s%s", a,b );
    printf("LCS = %s/n", build_lcs( str, a, b ));
}

×××××××××××××××调试版××××××××××××××××××××××

 

/*这个程序使用动态规划方法,求出两个字符串的最长公共序列,这里的公共序列于平常所想的不太一样,例如:BCDB就是ABCBDAB的一个子序列*/
/*
源程序出自《软件设计师》第二版page520*/
/*
经过初步调试.可以运行并得出正确结果*/
# include <stdio.h>
# include <string.h>

#define N 100/*
字符串的最大长度限制*/

char a[ N ],/*
第一个字符串*/
     b[ N ],/*
第二个字符串*/
     str[ N ];/*
用于存放a b的公共子序列*/

/*
计算两个序列的最长公共子序列的长度*/
int lcs_len( char *a, char *b, int c[][ N ] ){
    int m = strlen( a ),/*
计算两个序列的长度*/
        n = strlen( b ),
        i,/*
行指针*/
            j;/*
列指针*/
   
    for( i = 0; i <= m; i++ ) c[ i ][ 0 ] = 0;/*j==0
表示b串长度为0,无论a串多长,两者的公共子序列长度为0*/
    for( j = 1; j <= n; j++ ) c[ 0 ][ j ] = 0;/*
根据上一个类推*/
    for( i = 1; i <= m; i++ )/*
开始递推*/
        for( j = 1; j <= n; j++ )/*
若两序列的最后一个字符相同,那么公共子序列长度为去掉这最后一个字符后新生成的两个串的公共子序列长度+1*/
            if( a[ i - 1 ] == b[ j - 1 ])
                { c[ i ][ j ] = c[ i - 1 ][ j - 1 ] + 1;
                  printf("/nc[ %d ][ %d ] = c[ %d - 1 ][ %d - 1 ] + 1/n", i, j, i, j );}/**/
            else if( c[ i - 1 ][ j ] >= c[ i ][ j -1 ] )
                    { c[ i ][ j ] = c[ i - 1 ][ j ];
                      printf("/nc[ %d ][ %d ] = c[ %d - 1 ][ %d ]/n", i, j, i, j );
                    }/*
若两序列最后一个字符不同,那么他们最大子序列的长度为c[ i - 1][j]c[i][j-1]中较大的一个*/
                 else
                    {c[ i ][ j ] = c[ i ][ j -1 ];
                 printf("/nc[ %d ][ %d ] = c[ %d ][ %d - 1]/n", i, j, i, j);
                }
    /*
调试:将生成的数组方阵显示出来,m行,n*/
    printf("/n");
    printf("  ");
    for( j = 0; j <= n; j++ )printf("%d, ", j );/*
显示列标号*/
    printf("/n");
    for( i = 0; i <= m; i++ )
        {
         printf("%d: ", i );/*
显示行标号*/
            for( j = 0; j <= n; j++ )
            printf("%d, ", c[ i ][ j ] );
             printf("/n");/*
一行显示完,换下一行*/

        }

    return c[ m ][ n ];/*
结果:ab的最大子序列长度存在c[m][n]*/
}

/*
构造最长子序列函数*/
char *build_lcs( char s[], char *a, char *b ){/*
根据lcs_len()函数计算的结果,倒推出最长子序列,结果放在s[]*/
    int k,/*a
b最长子序列的长度*/
        i = strlen( a ),/*a
串长度*/
        j = strlen( b ),/*b*/
        c[ N ][ N ];/*
存放全部的计算过程,动态规划,体现在这个地方,中间计算结果都在这里*/
    k = lcs_len( a, b, c );/*
c[][]传给lcs_len()计算并求出长度,将中间结果放在c[][]*/
    s[ k ] = '/0';/*s
串的结束标记*/
    while( k > 0 )/*
开始倒推*/
        if( c[ i ][ j ] == c[ i - 1 ][ j ] ) i --;
        else if( c[ i ][ j ] == c[ i ][ j -1 ]) j--;
                else
                {    s[ --k ] = a[ i - 1 ];/*
将一个公共字符存入s*/
                     i--;
                     j--;
                }
    return s;
}

/*
测试程序*/
void main(){
    printf("Enter two string(<%d)!/n", N );
    scanf( "%s%s", a,b );
    printf("LCS = %s/n", build_lcs( str, a, b ));
}
转载于http://babylzu.blog.163.com/blog/static/81258022008867534445/

给定两个序列
X = { x1 , x2 , ... , xm }
Y = { y1 , y2 , ... , yn }
求X和Y的一个最长公共子序列

举例
X = { a , b , c , b , d , a , b }
Y = { b , d , c , a , b , a }
最长公共子序列为
LSC = { b , c , b , a }

分析:

最长公共子序列问题具有最优子结构性质


X = { x1 , ... , xm }
Y = { y1 , ... , yn }
及它们的最长子序列
Z = { z1 , ... , zk }

1、若 xm = yn , 则 zk = xm = yn,且Z[k-1] 是 X[m-1] 和 Y[n-1] 的最长公共子序列
2、若 xm != yn ,且 zk != xm , 则 Z 是 X[m-1] 和 Y 的最长公共子序列
3、若 xm != yn , 且 zk != yn , 则 Z 是 Y[n-1] 和 X 的最长公共子序列

由性质导出子问题的递归结构

当 i = 0 , j = 0 时 ,        c[i][j] = 0
当 i , j > 0 ; xi = yi 时 ,  c[i][j] = c[i-1][j-1] + 1
当 i , j > 0 ; xi != yi 时 , c[i][j] = max { c[i][j-1] , c[i-1][j] }

////////////////////////////////////////
这种分析方法我总得比较有用,值得保存,所以就从book
 ----《计算机机算法设计与分析》电子工业出版社
中摘录出来,如果不明白,可以看一看原作。
////////////////////////////////////////

// 书中只有关键部分的代码,现在已经补全
// 源程序

#include "iostream.h"
#include "iomanip.h"

#define max 100

void LCSLength( int m , int n , char *x , char *y , char *b )
{
 int i , j , k;
 int c[max][max];

 for( i = 1 ; i <= m ; i++ )
 {
  c[i][0] = 0;
 }
 for( i = 1 ; i <= n ; i++ )
 {
  c[0][i] = 0;
 }

 for( i = 1 ; i <= m ; i++ )
 {
  for( j = 1 ; j <= n ; j++ )
  {
   if( x[i-1] == y[j-1] )
   {
    c[i][j] = c[i-1][j-1] + 1;
    k = i * ( n + 1 ) + j;
    b[k] = '//';
   }
   else if( c[i-1][j] >= c[i][j-1] )
   {
    c[i][j] = c[i-1][j];
    k = i * ( n + 1 ) + j;
    b[k] = '|';
   }
   else
   {
    c[i][j] = c[i][j-1];
    k = i * ( n + 1 ) + j;
    b[k] = '-';
   }
  }
 }

}
void LCS( int i , int j , char *x , char *b , int width )
{
 if( i == 0 || j == 0 )
  return;
 int k = i * ( width + 1 ) + j;
 if( b[k] == '//' )
 {
  LCS( i - 1 , j - 1 , x , b , width );
  cout<<x[i]<<endl;
 }
 else if( b[k] == '|' )
 {
  LCS( i - 1 , j , x , b , width );
 }
 else
 {
  LCS( i , j - 1 , x , b , width );
 }
}

void main()
{
 char x[max] = { 'a' , 'b' , 'c' , 'b' , 'd' , 'a' , 'b' };
 char y[max] = { 'b' , 'd' , 'c' , 'a' , 'b' , 'a' };
 int m = 7;
 int n = 6;
 char b[max] = { 0 };

 LCSLength( m , n , x , y , b );
 LCS( m , n , x , b , n );

 cout<<endl<<endl;
}

////////////////////////////////////////
参考资料:
 《计算机算法分析与设计》电子工业出版社

 

 

 

作者Blog:http://blog.csdn.net/zeroDspace/


原创粉丝点击