问题定义
一个字符串S,去掉零个或者多个元素所剩下的子串称为S的子序列。
最长公共子序列就是寻找两个给定序列的子序列,
该子序列在两个序列中以相同的顺序出现,但是不必要是连续的。
例如序列X=ABCBDAB,Y=BDCABA。
序列BCA是X和Y的一个公共子序列,但是不是X和Y的最长公共子序列,
子序列BCBA是X和Y的一个LCS,序列BDAB也是。
寻找LCS的一种方法是枚举X所有的子序列,
然后注意检查是否是Y的子序列,并随时记录发现的最长子序列。
假设X有m个元素,则X有2^m个子序列,指数级的时间,对长序列不实际。
动态规划思路
假设dp[i][j]是字符串x、y的前i个字符和前j个字符的最长公共子串长度,则有:
1. dp[i][j] = 1+ dp[i-1][j-1] if(x[i-1] == y[j-1])
2. dp[i][j] = max{dp[i-1][j] , dp[i][j-1]} if(x[i-1] ≠ y[j-1])
边界条件
dp[i][j] = 0 (if i==0 || j==0)
#include<iostream>
#include<string>
using namespace std;
int max(int a, int b)
{
return a > b ? a : b;
}
int dp(int** dp,const char *X,const char* Y, int nXLength, int nYLength)
{
for (int i = 0; i <= nXLength; i++)
{
for (int j = 0; j <= nYLength; j++)
{
if (0 == i || 0 == j)
{
dp[i][j] = 0;
continue;
}
if (X[i-1] == X[j-1])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else
{
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[nXLength][nYLength];
}
int main()
{
char x[] = "abcfd";
char y[] = "abcdffd";
int nXLength = strlen(x);
int nYLength = strlen(y);
int ** dpTable = new int*[nXLength + 1]; //dp[i][j] 表示x序列与y序列前i、j个元素的最长公共子序列
for (int i = 0; i < nXLength + 1; i++)//构造用于保存状态dp[i][j]的二维数组
{
dpTable[i] = new int[nYLength + 1];
}
int lcsLength = dp(dpTable, x, y, nXLength, nYLength);
cout <<"最长公共子串长度为 "<< lcsLength << endl;
int i = 0;
int j = nXLength;
int k = nYLength ;
char *strLcs = new char[lcsLength+1]; //根据保存的状态表,打印出最大公共子串
strLcs[lcsLength] = '\0'; //由于是逆序寻找的,这里通过将字符逆序插入strLcs中,最后顺序打印
while (i < lcsLength)
{
if (x[j - 1] == y[k - 1])
{
strLcs[lcsLength-i-1] = x[j-1];
j -= 1;
k -= 1;
++i;
}
else
{
if (dpTable[j-1][k] >= dpTable[j][k-1])
{
j -= 1;
}
else
{
k -= 1;
}
}
}
cout << strLcs << endl;
system("pause");
for (int i = 0; i < nXLength + 1; i++)
{
delete[] dpTable[i];
}
delete[] dpTable;
delete[] strLcs;
return 0;
}