目录
从题目出发分析如何用动态规划求解最长公共子序列问题
题目
给定两个字符串A和B,返回两个字符串的最长公共子序列的长度。例如,A="1A2C3D4B56”,B="B1D23CA45B6A”,”123456"或者"12C4B6"都是最长公共子序列。
给定两个字符串A和B,同时给定两个串的长度n和m,请返回最长公共子序列的长度。保证两串长度均小于等于300。
测试样例:
"1A2C3D4B56",10,"B1D23CA45B6A",12
返回:6
题目分析
状态
dp[i][j],表示A[0,...i]和B[0,...j]的最长公共子序列长度。
边界值讨论
当i = 0时,dp[i][j]求的是A[0]和B[0,...j]的最长公共子序列长度,遍历B数组,如B[k] == A[0],也就是在B中找到了等于A[0]的值,最长公共子序列就是A[0],则dp[0][k,k+1,...m] = 1;
同理,当j = 0时,dp[i][j]求的是A[0,...i]和B[0]的最长公共子序列长度,遍历A数组,如A[t] == B[0],最长公共子序列就是B[0],则dp[t,t+1,...n][0] = 1;
其他情况讨论
求一般情况下的dp[i][j],只可能出现两种情况:
1.如果A[i] == B[j],dp[i][j] = dp[i-1][j-1]+1; 如‘ABCD’和‘ABDD’的最长公共子序列长度等于‘ABC’和‘ABD’的最长公共子序列长度+1,因为末尾元素D一定会包含在'ABCD'和'ABDD'的最长公共子序列中。
2.如果A[i]!=B[j],那么,A[0,...i]和B[0,...j]的最长公共子序列要么从A[0,..i]和B[0,...j-1]中取得,要么从A[0,..i-1]和B[0,...j]中取得;比较两个值中的最大值,即为dp[i][j]的值;如‘ABCD’和‘ABD4’中最长公共子序列的长度等于‘ABC’和‘ABD4'的最长公共子序列长度,要么等于‘ABCD’和‘ABD’的最长公共子序列长度,我们只需要取最大值即可。
代码实现
class LCS {
public:
int findLCS(string A, int n, string B, int m) {
// write code here
//dp[i][j]表示A[0,...i]和B[0,...j]的最长公共子序列的长度
int dp[301][301] = {0};
for(int i = 0;i<n;i++){//第一列中,找出A[i] = B[0],dp[i][0] = 1,且dp[i+1,...n][0] = 1;
if(A[i]==B[0]) //
{
for(int j = i;j<n;j++){
dp[j][0] = 1;
}
}
}
for(int i = 0;i<m;i++){//第一行中,找出A[0] = B[i],dp[0][i] = 1,且dp[0][i+1,...m] = 1;
if(A[0]==B[i]) //
{
for(int j = i;j<m;j++){
dp[0][j] = 1;
}
}
}
//接下来考虑一般情况,dp[i][j]可能来自哪些情况:
// A[i] != B[j] ,A[0...i]和B [0,...j-1]的最长公共子序列为dp[i][j-1]
//如果A[i] == B[j],dp[i][j] = dp[i-1][j-1]+1
// A[i] != B[j] ,A[0...i-1]和B [0,...j]的最长公共子序列为dp[i-1][j]
int max = 0;
for(int i = 1;i<n;i++){
for(int j = 1;j<m;j++){
if(A[i] == B[j])
max= dp[i-1][j-1]+1;
else{
max = (dp[i][j-1] > dp[i-1][j] ? dp[i][j-1]: dp[i-1][j]);
}
dp[i][j] = max;
}
}
return dp[n-1][m-1];
}
};