最长公共子序列问题

动态规划之最长公共子序列问题
1.问题描述:
  • 输入两个字符串
  • 输出最长公共子序列的长度。若最大公共子序列的长度大于0,则还会输出一个最大公共子序列。
  • 举例:
    cnblogs与belong
    4
    blog
    (注意:子序列是不连续的,而子串是连续的。但它们的字符出现顺序均与所输入字符串的字母先后顺序一致。)

2.求解最大公共子序列的长度
  • 思路分析: 假设k为最长公共子序列的长度。
    (1).如果s1中的第i个元素等于s2中的第j个元素,则此时k的值为s1[m-1]与s2[n-1]的k再加1的值。
    (2).如果s1中的第i个元素不等于s2中的第j个元素,则此时k的值为max(s1[m-1]与s2[n]的k,s1[m]与s2[n-1]的k)。
    (3).因此,求解k的问题则变成递归求解的两个子问题。但是,上述的递归求解的办法中,重复的子问题多,效率低下。改进的办法——用空间换时间,用数组保存中间状态,方便后面的计算。这就是动态规划的核心思想了。
  • 求解k的状态转移方程
    用i,j遍历两个字符串x,y,定义一个整形二维数组c[m][n],m表示x串长,n表示y串长。c[m][n]中的元素c[i][j]表示x中的前i个元素(注意:是从x[0]到x[i-1]这i个元素)和y中的前j个元素的最大公共子序列长度。
    最大公共子序列的长度问题
  • 源代码:

#include <stdio.h>
#include <string.h>
#define MAX 500

int main()

{
char   s1[MAX],s2[MAX];
int     c[MAX][MAX];
int     i,j,m,n;
gets(s1);
gets(s2);
m=strlen(s1);
n=strlen(s2);
for(i = 0; i <m+1; i++)
       c[i][0]=0;       
 for(j = 0; j <n+1; j++)
       c[0][j]=0;        
for(i=1;i<=m;i++)
{
	for(j=1;j<=n;j++)
	{
		if(s1[i-1]==s2[j-1])  //由于c[][]的0行0列没有使用,c[][]的第i行元素对应str1的第i-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];
		else
		        c[i][j]=c[i][j-1];
	
	}
}
printf("%d\n",c[m][n]);
return   0;
}

3.求解最大公共子序列长度以及一个最大公共子序列
  • 思路分析:再定义一个二维整形数组,以c[m][n]数组为判断条件,判断c[i][j]是否等于0,若等于0,则向左上方移一步,输出(i-1)位置时s1字符串中的元素。若等于1,则上移(左移)一步;若等于-1,则左移(上移)一步。但因为是要正序输出,所以需要先递归调用,遍历完字符串后再将符合条件的字符元素从首输出直到尾。
  • 源代码:

#include <stdio.h>
#include <string.h>
#define MAX 500
int flag[MAX][MAX];
char s1[MAX];

void    print(int   m,int   n)
{
   if(m==0||n==0)  
      return;
   if(flag[m][n]==0)
    {
	      print(m-1,n-1);
	      printf("%c",s1[m-1]);//从后面开始递归,所以要先递归到子串的前面,然后从前往后开始输出子串
    }	                       //c[][]的第i行元素对应s1的第i-1个元素
   else
        if(flag[m][n]==1)
               print(m-1,n);
   else
              print(m,n-1);
 }
   int   main() 
   {
       char   s2[MAX];
       int     c[MAX][MAX];
       int     i,j,m,n;
       printf("请输入第一个字符串:");
       gets(s1);
       printf("请输入第二个字符串:");
       gets(s2);
       m=strlen(s1);
       n=strlen(s2);
    for(i = 0; i <m+1; i++)
	     c[i][0]=0;        //第0列都初始化为0
    for(j = 0; j <n+1; j++)
	     c[0][j]=0;        //第0行都初始化为0
    for(i=1;i<=m;i++)
   {
	      for(j=1;j<=n;j++)
	    {
		     if(s1[i-1]==s2[j-1])  //由于c[][]的0行0列没有使用,c[][]的第i行元素对应str1的第i-1个元素
		    {
		         c[i][j]=c[i-1][j-1]+1;
		         flag[i][j]=0;
		    }
	    	else
		        if(c[i-1][j]>c[i][j-1])
		    {
		        c[i][j]=c[i-1][j];
		        flag[i][j]=1;
		    }
		   else
		   {
		        c[i][j]=c[i][j-1];
		        flag[i][j]=-1;
		    }
	  }
    }
printf("最长公共子序列的长度为%d\n",c[m][n]);
print(m,n);
return   0;
}

4.图表注释

仍然以"cnblogs"和"belong"为例来用图表解释一下
图表解释最大公共子序列

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值