问题
给定两个字符串s1s2...sn和t1t2...tm求出这两个字符串的最长公共子序列(LCS)的长度。
样例
n=4
m=4
s="abcd"
t="becd"
输出
3(#指"bcd")
题目理解
给定两个字符串,让你得出(两个字符串分别从头开始一个一个字符检查)有哪些字符是按顺序匹配的(每个字符只能匹配一次)。
比如对于样例,s中的第一个字符a在t中没有匹配的,跳过,到s中第二个字符b。t中第一个字符是b,匹配数为1,将b视为消去(不能再用于匹配)。然后s中第三个字符c和t中第二个字符e不匹配,和第三个字符c匹配,匹配数为2。s中第四个字符d又和t中第四个字符d匹配,匹配数为3.
最终结果为3。
解答
首先定义一个数组dp[MAX_N+1][MAX_M+1]
数组dp[i][j]存储s1s2...si和t1t2...tj字符串的最长公共子序列(LCS)。
递推公式为
dp[i+1][j+1]的结果可以有以下情况
1.当s[i+1]==t[j+1]时
dp[i+1][j+1]的值一定比dp[i][j]多1,多的那一个匹配可能是s[i+1]和t[j+1]配对,也可能不是,但是一定能保证多1,不多不少。
2.当s[i+1]!=t[j+1]时
这时可以再细分为两种情况
one:
看s1s2...si和t1t2...tjt(j+1)的匹配结果
two:
看s1s2...sis(i+1)和t1t2...tj的匹配结果
找大的那个
得到的递推关系为
dp[i+1][j+1]=
dp[i][j]+1 ( if s(i+1)==t(j+1))
max(dp[i][j+1],dp[i+1][j]) (else)
代码为
代码这里有个小坑,s和t的索引好像和dp的索引对不太上,后面解释
int n,m;
char s[MAX_N],t[MAX_M];
int dp[MAX_N+1][MAX_M+1];
void solve()
{
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(s[i]==s[j])
{
dp[i+1][j+1]=dp[i][j]+1;
}
else
{
dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]);
}
}
}
printf("%d\n",dp[n][m]);
}
这个代码我当时理解上的最大问题就是dp的索引和s,j的索引对不上。
以下是我对这个现象的理解:
我认为是这个代码想要从头(第一个元素)执行到尾用的一个小技巧。毕竟递推式是dp[i+1][j+1],i和j都是从0开始的。遵循dp定义,应该从dp[1][1]开始。而s的索引是从0开始的。所以如果你仔细研究代码的话你会发现dp[i+1][j+1]对应的是s[i]和t[j]相比的时候,两者索引差1。
所以这时dp[i+1][j+1]的意义是s1s2...si和t1t2...tj字符串的最长公共子序列(LCS),这点和前面说的递推式不一样。
这样还有一个妙处,dp是全局变量,默认值是0,正好dp[1][1]用的dp[0][0]是默认值0,很妙。