简述: LCS问题,即最长公共子序列问题,给定两个序列X={x1, x2, …, xm}和Y={y1, y2, …, yn},求X、Y最长的公共子序列。与LIS类似,LCS也是可以不连续的。
解题思路:本人觉得在这个问题上算法导论讲的很好,所以在此我主要是整理。
1、首先我们来考虑暴力搜索求解的方法,我们要暴力枚举X的所有子序列,然后再看看是不是也是Y的子序列,这样的方法,显然时间复杂度是指数级的,所以并不可取,但是,我们是否能从中看到些什么呢?
2、我们来看看LCS是不是具有最优子结构:
之前说的暴力求解,在程序中怎么表达呢?考虑一个序列Z={z1, z2, …, zk},他是X、Y的一个LCS序列:
如果xm==yn,就意味着xm肯定在Z序列里,而且是zk;
如果xm!=yn,那么说明xm和yn至多有一个在Z里面,所以这里X、Y的LCS就有两种可能性:X(1…m-1)与Y(1…n)的LCS和X(1…m)与Y(1…n-1)的LCS;
于是有最优子结构!
3、从上面就可以看出来了,没错,就是深搜!但是,太慢了。所以,记忆化一下就行了!
于是……用dp[i][j]表示X(1…i)和Y(1…j)的LCS,那么有状态转移方程:
if(x[i]==y[j])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
完整代码:(可以用POJ_1458测试一下)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int m,n,x[1005],y[1005],ans,dp[1005][1005];
char st1[1005],st2[1005];
int main()
{
while(~scanf("%s %s",st1,st2))
{
m=strlen(st1),n=strlen(st2);
for(int i=0;i<m;i++)
x[i+1]=st1[i];
for(int i=0;i<n;i++)
y[i+1]=st2[i];
ans=0;
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if(x[i]==y[j])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
printf("%d\n",dp[m][n]);
}
return 0;
}
总结:
1、之前一直不知道LCS是啥,今天算是了解了一下;
2、果然深搜是王道啊!