用动态规划求最长公共子序列其实核心思想很简单,就是分3种情况判断,根据不同情况做相应递归调用即可。如上图3种情况:i,j分别表示字符串1、2的当前相对长度,s[i,j]表示字符串1的前i个字符与字符串2的前j个字符所拥有的最长公共子序列的长度;
第一种情况:很显然当i=0 || j=0时,s[i,j]==0;
第二种:当字符串1、2的第i、j个字符相等时,则c[i,j]的长度直接为s[i-1,j-1]+1;
第三种:也就是当字符串1、2的第i、j个字符不相等时,分别前推一个字符计算s[],并取较大者;
核心就是3种情况的判断及相应调用的处理;
#include<stdio.h>
#include<string>
using namespace std;
char c[20]; //记录子序列字符,便于输出lcs
int num=0; //子序列个数,可能有多个lcs
//求最长公共子序列的长度
int lcs_Len(string x,string y,int b[20][20]){
int len1=x.length();
int len2=y.length();
int s[20][20]; //存储:x前i个字符串与y前j个字符串对应的lcs
int i,j;
//情况一:
for(i=0;i<=len1;i++)
s[i][0]=0;
for(j=0;j<=len2;j++)
s[0][j]=0;
for(i=1;i<=len1;i++)
for(j=1;j<=len2;j++){
if(x[i-1]==y[j-1]){
s[i][j]=s[i-1][j-1]+1;//情况二
b[i][j]=1;
}
else{ //情况3
if(s[i-1][j]>s[i][j-1]){
s[i][j]=s[i-1][j];
b[i][j]=2;
}
else{
s[i][j]=s[i][j-1];
b[i][j]=3;
}
}
}
//代价矩阵表
for(i=1;i<=len1;i++){
for(j=1;j<=len2;j++){
printf("%d ",s[i][j]);
}
printf("\n");
}
return s[len1][len2];
}
//输出lcs
void lcs_Show(int i,int j,string x,int b[20][20],int len,int lcsLen){
if(i==0 || j==0){ //情况一
return;
}
if(b[i][j]==1){ //情况二
c[len--]=x[i-1];
lcs_Show(i-1,j-1,x,b,len,lcsLen);
}else{ //情况三
if(b[i][j]==2)
lcs_Show(i-1,j,x,b,len,lcsLen);
else
lcs_Show(i,j-1,x,b,len,lcsLen);
}
}
void main(){
/*lcs test
只实现了输出其中一个最长公共子序列
*/
string s1="ABCBDAB";
string s2="BDCABA";
int lcsLen; //最长子序列长度
int b[20][20]; //二维数组记录标记
lcsLen=lcs_Len(s1,s2,b);
// printf("%d\n",lcsLen);
lcs_Show(s1.length(),s2.length(),s1,b,lcsLen,lcsLen);
for(int k=1;k<=lcsLen;k++)
printf("%c",c[k]);//输出lcs
printf("\n");
}
效果图: