最长公共子序列问题
动态规划之最长公共子序列问题
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"为例来用图表解释一下