1.问题
给定向量
X
=
<
X
1
,
X
2
,
.
.
.
,
X
n
>
{X=<X_1,X_2,...,X_n>}
X=<X1,X2,...,Xn>
Y
=
<
Y
1
,
Y
2
,
.
.
.
Y
m
>
{Y=<Y_1,Y_2,...Y_m>}
Y=<Y1,Y2,...Ym>
求解X和Y的最长公共子序列
2.解析
设dp[i][j]为X的前i位和Y的前j位的LCS的长度,path[i][j]为记录dp[i][j]所采取的决策,1为删掉X的第i位,2为删掉Y的第j位,3为分别删掉X,Y的i,j位。
由此可以推出dp方程,当i=0||j=0时,dp[i][0]=dp[0][j]=0
;当i>=1&&j>=1时,再分类讨论,当X[i]==Y[j]时,dp[i][j]=dp[i-1][j-1]+1
;当X[i]!=Y[j]时,dp[i][j]=max(dp[i-1][j],dp[i][j-1])
,同时根据dp[i][j]的选择策略决定path[i][j]的值,当dp[i-1][j]>dp[i][j-1],说明在X[i],Y[j]不相等时,当前序列可以选择删除i位得到dp[i-1][j]的LCS的长度,dp[i-1][j]<=dp[i][j-1]同理。
例子:
3.设计
int dp[MAXN][MAXN], path[MAXN][MAXN];
stack<int>sta;
void find_LCS(int X[], int Y[], int n, int m)//递归求解LCS序列中具体的数
{
if (!n || !m) return;
if (path[n][m] == 1)//1代表需删除X序列中的最后一位
find_LCS(X, Y, n - 1, m);
else if (path[n][m] == 2)//2代表需删除Y序列中的最后一位
find_LCS(X, Y, n, m - 1);
else if (path[n][m] == 3)//3代表X[n-1]=Y[m-1]
{
sta.push(X[n - 1]);
find_LCS(X, Y, n - 1, m - 1);
}
}
void LCS(int X[], int Y[], int n, int m)//求解LCS
{
memset(dp, 0, sizeof(dp));//初始化
memset(path, 0, sizeof(path));
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
if (X[i - 1] == Y[j - 1])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
path[i][j] = 3;//代表X[i-1]和Y[j-1]相等
}
else
{
if (dp[i - 1][j] > dp[i][j - 1])
{
dp[i][j] = dp[i - 1][j];
path[i][j] = 1;//删除X[i-1]更优
}
else
{
dp[i][j] = dp[i][j - 1];
path[i][j] = 2;//删除Y[j-1]更优
}
}
}
printf("最长公共子序列的长度=%d\n", dp[n][m]);
find_LCS(X, Y, n, m);//递归求解LCS序列中具体的数
while (!sta.empty()) //输出
{
printf("%d\t", sta.top());
sta.pop();
}
}
4.分析
从代码中我们很容易得出,双重循环,时间复杂度为O(nm),并且寻找LCS序列的递归算法的时间复杂度也小于O(nm),所以该算法总的时间复杂度为O(nm)