问题描述
字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。
令给定的字符序列X=(x0,x1,…,xm-1),序列Y=(y0,y1,…,yk-1)是X的子序列,存在X的一个严格递增下标序列(i0,i1,…,ik-1),使得对所有的j=0,1,…,k-1,有 =yj。
问题求解
若设A=(a0,a1,…,am-1)(含m个字符),B=(b0,b1,…,bn-1)(含n个字符),设Z=(z0,z1,…,zk-1)(含k个字符)为它们的最长公共子序列。不难证明有以下性质:
如果am-1=bn-1,则zk-1=am-1=bn-1,且(z0,z1,…,zk-2)是(a0,a1,…,am-2)和(b0,b1,…,bn-2)的一个最长公共子序列。
如果am-1≠bn-1且zk-1≠am-1,则(z0,z1,…,zk-1)是(a0,a1,…,am-2)和(b0,b1,…,bn-1)的一个最长公共子序列。
如果am-1≠bn-1且zk-1≠bn-1,则(z0,z1,…,zk-1)是(a0,a1,…,am-1)和(b0,b1,…,bn-2)的一个最长公共子序列。
定义二维动态规划数组dp,其中dp[i][j]为子序列(a0,a1,…,ai-1)和(b0,b1,…,bj-1)的最长公共子序列的长度。
每考虑字符a[i]或b[j]都为动态规划的一个阶段(共经历约m×n个阶段)。
对应的状态转移方程如下:
显然,dp[m][n]为最终结果。
当dp[i][j] ≠ dp[i][j-1](左边)并且dp[i][j] ≠ dp[i-1][j](上方)值时:a[i-1]=b[j-1]将a[i-1]添加到LCS中。
dp[i][j]=dp[i][j-1]:与左边相等 j–
dp[i][j]=dp[i-1][j]:与上方相等 i–
与左边、上方都不相等:a[i-1]或者b[j-1]属于LCS i–,j–
代码
int m, n;
string a, b;
int dp[MAXN][MAXN];
vector<char> subs;
void LCSlength()
{
int i, j;
for (i = 0; i <= m; i++)
dp[i][0] = 0;
for (j = 0; j <= n; j++)
dp[0][j] = 0;
for (i = 1; i <= m; i++)
{
for (j = 1; j <= n; j++)
{
if (a[i - 1] == b[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
}
}
}
void Buildsubs()
{
int k = dp[m][n];
int i = m;
int j = n;
while (k > 0)
{
if (dp[i][j] == dp[i - 1][j])
i--;
else if (dp[i][j] == dp[i][j - 1])
j--;
else
{
subs.push_back(a[i - 1]);
i--; j--; k--;
}
}
}
算法分析
LCSlength算法中使用了两重循环,所以对于长度分别为m和n的序列,求其最长公共子序列的时间复杂度为O(m×n)。空间复杂度为O(m×n)。