首页 > 代码库 > 算法实验--最长子序列
算法实验--最长子序列
一、实验目的: 熟悉掌握动态规划法设计技术 二、实验要求: 1、按教材所授内容要求,完成“最长公共子序列问题”算法。得到一个完整正确的程序。 2、问题规模:不少于100 3、输出最终结果。 三、实验设备: PC机一台 Vc++6.0编译软件一套 四、问题描述: 若给定序列X={x1,x2,…,xm},则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:zj=xij。例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最长公共子序列。 例如:“ABCBDAB” 和 “BDCABA” 的最长公共子序列为: BCBA 或者“ABHC898987fjgij”和“ABCIEJGF89895f55fg”的公共子序列为:ABC8989fg 五、算法分析: 设定有两个序列,分别是X(m)和Y(n),即是M个元素的X序列和有N个元素的Y序列,设序列X={x1,x2,x3,....xm}和Y={y1,y2,y3.....yn}的最长公共子序列Z={z1,z2,z3....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是X和Y(n-1)的最长公共子序列。 其中,X(m-1)={x1,x2,x3......x(m-1)};Y(n-1)={y1,y2,y3......y(n-1)}; Z(k-1)={z1,z2,z3.......z(k-1)}。 证明:(1)反证法。若zk!=xm,则{z1,z2,z3...zk,zm}是X和Y的长度为k+1的公共子序列。这与Z是X和Y的最长公共子序列矛盾。因此,必有zk=xm=yn.由此可知Z(k-1)是X(m-1)和Y(n-1)的长度为k-1的公共子序列。若X(m-1)和Y(n-1)有长度大于k-1的公共子序列W,则将Xm加在其尾部产生X和Y的长度大于k的公共子序列。此为矛盾。故Z(k-1)是X(m-1)和Y(n-1)的最长公共子序列。 (2)由于zk!=xm,Z是X(m-1)和Y的公共子序列。若X(m-1)和Y有长度大于k的公共子序列W,则W也是X和Y的长度大于k的公共子序列。这与Z是X和Y的最长子序列矛盾。由此可知,Z是X(m-1)和Y的最长公共子序列。 (3)本实验采用随机生成【0-9】【A-Z】【a-z】之间的数字和字母和手动输入字符串X(M)和Y(N)两种方式求得最长子序列的解。 用到的函数有: void InputLCS(char *x,char *y,int B[][N+1]).通过手动输入求解函数。 void RandLCS(const int m,const int n,char *x,char *y,int B[][N+1]).通过随机生成字符串求解。 void LCSLength(int m,int n,char x[M+1],char y[N+1],int b[M+1][N+1]), void LCS(int i,int j,char x[M+1],int b[M+1][N+1])。此两个函数式求解关键函数,前者求出最优解并将子序列长度存放在局部变量c[m][n]中并显示出来,后者将最长子序列以字符串的形式显示在屏幕上。 void printRand(char x[],const m)。是随机生成字符串并输出的函数,是随机生成字符串方式的重要函数。
六、代码: #include <stdio.h> #include<stdlib.h> #include<time.h> #include<string.h> #define M 150 #define N 150 void LCSLength(int m,int n,char x[M+1],char y[N+1],int b[M+1][N+1]); void LCS(int i,int j,char x[M+1],int b[M+1][N+1]); void printRand(char x[],const m); void InputLCS(char *x,char *y,int B[][N+1]); void RandLCS(const int m,const int n,char *x,char *y,int B[][N+1]); void main(){ int choose; char X[M+1],Y[N+1]; int B[M+1][N+1]; printf("---------请选择求最长子序列方式----------\n"); printf("---------------1.手动输入----------------\n"); printf("---------------2.随机生成----------------\n"); printf("---------------0.退出--------------------\n"); scanf("%d",&choose); switch(choose) { case 1: InputLCS(X,Y,B); break; case 2: RandLCS(M,N,X,Y,B); break; case 0: exit(0); } printf("\n"); }
//生成随机数并显示出来,随机生成部分 void printRand(char x[],const int m) { for(int i=0;i<m;i++) { x[i]=(rand()%75+48);//0-9(48-57),A-Z(65-90),a-z(97-122) //若在数字与大写字母之间则+7得到大写字母 if(x[i]>57&&x[i]<65)x[i]=x[i]+7; //若在大小写字母之间则+6得到小写字母 else if(x[i]>90&&x[i]<97)x[i]=x[i]+6; printf("%c",x[i]); } } //处理随机生成方式求最长子序列 void RandLCS(const int m,const int n,char *x,char *y,int B[][N+1]) { srand(time(NULL)); printf("生成字符串a(长度%d):\n",m); printRand(x,m); printf("\n生成字符串b(长度%d):\n",n); printRand(y,n); LCSLength(M,N,x,y,B); printf("\n公共子序列结果:"); LCS(M,N,x,B); }
//处理手动输入方式求最长子序列 void InputLCS(char *x,char *y,int B[][N+1]) { int lx,ly; printf("输入字符串a:\n"); scanf("%s",x+1); printf("输入字符串b:\n"); scanf("%s",y+1); lx=strlen(x+1); ly=strlen(y+1); printf("\n字符串a长度la=%d; 字符串b长度lyb=%d",lx,ly); LCSLength(lx,ly,x,y,B); printf("\n公共子序列结果:"); LCS(lx,ly,x,B); }
//求出子序列的最优解存放在c[m][n]中 void LCSLength(int m,int n,char x[],char y[],int b[M+1][N+1]) { int i,j; int c[M+1][N+1]; 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; } 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; } } printf("\n公共子序列长度length:%d",c[m][n]); } //输出最长子序列的结果 void LCS(int i,int j,char x[],int b[M+1][N+1]) { if (i==0 || j==0) return; if (b[i][j]==1) { LCS(i-1,j-1,x,b); printf("%c",x[i]); } else if(b[i][j]==2) LCS(i-1,j,x,b); else LCS(i,j-1,x,b); } 七、调试及运行: 1.手动输入字符串运行结果:
2.自动生成字符串方式,其中生成50个元素效果如下:
3.和结果2相似,生成元素为100个:
4.生成元素个数为150时,效果如下:
|
算法实验--最长子序列