求最长公共子序列
算法思想
1、定义dp [i][j]:表示字符串序列A的前i个字符组成的序列Ax和字符串序列B的前j个字符组成的序列By之间的最长 公共子序列L(i,j )的长度(m ,n分别为Ax和By的长度,i<=m,j<=n)
2、如果Ax [i] =By [j],那么Ax与By之间的最长公共子序列L( i,j )的最后一项一定是这个元素,
所以dp [i][j] = dp[i-1][j-1] + 1。
3、如Ax[i] != By[j],设LCS(i-1,j-1)是L( i -1, j-1 )最后一个元素,或者L(i-1,j-1)是空序列,
则t!= Ax[i]和t!=By[j]至少有一个不成立。
(1)当LCS(i-1,j-1) != Ax[i]时,dp[i][j]= dp[i-1][j];
(2)当LCS(i-1,j-1) != By[j]时,ap[i][j]= dp[i][j-1];
所以dp[i][j]= max ( dp[i-1][j],dp[i][j-1] )。
4、初始值为:dp[0][j] = dp[i][0] = 0.
5、题意要求求出任意一个最长公共子序列,这点要如何实现呢?
仍然考虑上面的递推式,L(i,j)的最后一个元素LCS( i,j )的来源有三种情况,定义数组flag[MAXN][MAXN]用以标记来的方向:
(1) dp[i][j] = dp[i-1][j-1] + 1,对应字符LCS( i-1,j-1)接上LCS( i,j),flag[i][j] = 1,表示从斜向上左方来;
(2) dp[i][j] = dp[i-1][j],对应字符LCS(i-1,j)接上LCS(i,j),flag[i][j] = 2,表示从上方过来;
(3) dp[i][j] = dp[i][j-1],对应字符LCS(I,j-1)接上LCS(i,j),flag[i][j] = 3,表示从左方过来。
我们在计算dp[i][j]时根据来源进行不同的标记,回溯就可以找到一个最长公共子序列。下图的答案是BCBA,长度4也符合。
#include<bits/stdc++.h>
char a[1010],b[1010]; //A,B两个串,注意下标是0开始存
int dp[1010][1010]; //最大公共子序列长度,注意下标是1开始存
int flag[1000][1000]; //标记来的方向,注意下标是1开始存
int main(){ //注意dp[i][j]是指a串[0~i-1]和b串[0~j-1]的LCS
while(gets(a)){ //获得A串,非空继续
gets(b); //获B串
memset(dp,0,sizeof(dp));//DP清空
memset(flag,0,sizeof(flag));//标志清空
int m = strlen(a); //得A串长
int n = strlen(b); //得B串长
int i,j; //定义循环变量
for(i = 1;i <= m;i++) //扫一次A串
for(j = 1;j <= n;j++){ //再扫一次B串
if(a[i-1] == b[j-1]){ //两串的最后一位匹配(理清DP定义,与串下标差1!)
dp[i][j] = dp[i-1][j-1] + 1;//然后DPIJ就可以在上一个DP基础上加一
flag[i][j] = 1; //然后打上方向1标记
}//上一步(i-1,j-1),斜向左上
else if(dp[i-1][j] > dp[i][j-1]){//如果不匹配,就直接取上方及左方格DP较大值
dp[i][j] = dp[i-1][j]; //dp(i-1,j)大就取dp(i-1,j)
flag[i][j] = 2; //方向从2来
}//上一步(i-1,j),上方
else { //如果不匹配,就直接取上方及左方格DP较大值
dp[i][j] = dp[i][j-1]; //dp(i,j-1)大就取dp(i,j-1)
flag[i][j] = 3; //方向从3来
}//上一步(i,j-1),左方
}
int k = 0; //构造开始
char c[1010]; //就是要输出的答案
while(m > 0 && n > 0){ //从终点按标记的方向反方向往回走,因为DP与FLAG是从1起存的,所以>0
if(flag[m][n] == 1){ //1号方向表示这个是新匹配
c[k++] = a[m - 1]; //打入C数组
m--; n--; //左上角来,MN下标都减一
}
else if(flag[m][n] == 2)m--;//2号方向上面来,行减少
else if(flag[m][n] == 3)n--;//3号方向左面来,列减少
}
for(i = k-1;i >= 0;i--)printf("%c",c[i]);//逆向输出
printf("\n");
}
return 0;
}
/*
ABCBDAB
BDCABA
BDAB
*/