题目六 最长公共子序列
题目描述
我们称一个字符的数组S为一个序列。对于另外一个字符数组Z,如果满足以下条件,则称Z是S的一个子序列:(1)Z中的每个元素都是S中的元素(2)Z中元素的顺序与在S中的顺序一致。例如:当S = (E,R,C,D,F,A,K)时,(E,C,F)和(E,R)等等都是它的子序列。而(R,E)则不是。
现在我们给定两个序列,求它们最长的公共子序列的长度。
关于输入
一共两行,分别输入两个序列
关于输出
一行,输出最长公共子序列的长度。
例子输入
ABCBDAB BDCABA
例子输出
4
解题分析
这个问题的具体描述是:给定两个序列,求它们的最长公共子序列的长度。
程序的主要思路如下:
-
首先,程序读取两个字符串,存储在
word1
和word2
中,然后计算它们的长度len1
和len2
。 -
然后,程序初始化一个二维数组
dp
,dp[i][j]
表示word1
的前i
个字符和word2
的前j
个字符的最长公共子序列的长度。 -
程序遍历所有可能的
i
和j
(从0到len1
和len2
)。-
如果
i
或j
为0,那么dp[i][j]
就等于0,因为空字符串与任何字符串的最长公共子序列的长度都是0。 -
如果
word1[i-1]
等于word2[j-1]
,那么dp[i][j]
就等于dp[i-1][j-1] + 1
。这是因为,当前的字符可以加入最长公共子序列。 -
如果
word1[i-1]
不等于word2[j-1]
,那么dp[i][j]
就等于dp[i][j-1]
和dp[i-1][j]
中的较大值。这是因为,当前的字符不能同时加入最长公共子序列,所以我们只能选择一个。
-
-
最后,
dp[len1][len2]
就是word1
和word2
的最长公共子序列的长度。
这个程序的时间复杂度是O(n^2),因为它需要遍历所有可能的i
和j
。如果字符串的长度非常大,那么这个程序可能会运行得比较慢。
代码实现
#include <iostream>
#include <cstring>
using namespace std;int dp[10005][10005];
char word1[10005],word2[10005];int main() {cin>>word1>>word2;int len1=strlen(word1),len2=strlen(word2);for(int i=0;i<=len1;i++)for(int j=0;j<=len2;j++){if(i==0 || j==0){dp[i][j]=0;}else{if(word1[i-1]==word2[j-1]){dp[i][j]=dp[i-1][j-1]+1;}else{dp[i][j]=max(dp[i][j-1],dp[i-1][j]);}}}cout<<dp[len1][len2]<<endl;return 0;
}
使用记忆搜索法解决问题
#include <iostream>
#include <cstring>
using namespace std;int dp[10005][10005];
char word1[10005],word2[10005];int f(int i,int j){if(i==0 || j==0){return 0;}if(dp[i][j]){return dp[i][j];}if(word1[i-1]==word2[j-1]){dp[i][j]=f(i-1,j-1)+1;}else{dp[i][j]=max(f(i-1,j),f(i,j-1));}return dp[i][j];
}int main() {cin>>word1>>word2;int len1=strlen(word1),len2=strlen(word2);cout<<f(len1,len2)<<endl;return 0;
}