因此,最长公共子序列问题的伪代码如下所示。
最长共同子序列(s1,s2):
如果字符串以相同的字母c开头,则返回的结果为c 加上s1和s2其余部分之间最长的公共子序列 (即s1和s2没有第一个字母)。例如, 中空"空心"之间最长的后续序列和"你好"是一个" h"再加上 " ollow"之间发现的最长的后续序列和" ello"。
否则,如果 字符串不以相同的字母开头,返回的时间越长 以下两个:s1和。之间最长的共同子序列 其余的s2(s2没有第一个字母),最长的共同点 s1的其余部分之间的子序列(s1没有第一个字母)和 S2。例如,最长共同子序列(" ollow"," ello")是 更长的最常见的子序列(" ollow"," llo")和 最长的共同后续(" llow"," ello")。
我不能得到的部分是当字符串不以相同的字母开头时,为什么我们采取(s1没有第一个字母,s2),(s1,s2没有第一个字母)。为什么我们在不匹配时递归地执行这些步骤?它只是一个难以理解的集合算法吗?这背后的原因是什么?
答案 0 :(得分:3)
当我们知道两个字符串的第一个字符不匹配时,很明显我们不能像我们在第一种情况中那样在最长的子序列中包含字符。因此,我们留下的显而易见的选择是忽略这两个字符,并为我们的子序列搜索两个字符串的其余部分。但是如果你考虑这个例子:“你好”和“ello”,你可以清楚地看到,如果我们忽略了第一个字符,我们基本上忽略了我们子序列的第一个字符(“Ello”)。所以我们选择了两个案例: 1.我们删除第一个字符串的第一个字符并在第二个字符串中搜索。 2.我们删除第二个字符串的第一个字符并在第一个字符串中搜索。
然后我们最多采用这两个。
答案 1 :(得分:3)
虽然@yash mahajan已经涵盖了所有内容,但我只想提供另一种思考方式。
通过两个字符串,假设你在字符串A(长度为m)的位置i和字符串B(长度为n)的位置j。
<强> 1。如果两个字符串的当前两个字符相同:
到目前为止最长的公共子序列=子串A [0 ... i-1]和子串B [0 ... j-1] + 1之间的最长公共子序列。
<强> 2。如果两个字符不同:
最长公共子序列= Max(子串A [0 ... i-1]和字符串B之间的最长公共子序列,字符串A和子串B [0 ... j-1]之间的最长公共子序列)
如果你阅读了这些代码,你会有一个更清晰的想法。
public class Solution {
public int longestCommonSubsequence(String A, String B) {
if(A == null || B == null || A.length() == 0 || B.length() == 0) {
return 0;
}
int m = A.length();
int n = B.length();
int[][] commonSubsequenceLength = new int[m + 1][n + 1];
for(int i = 1; i <= m; i++) {
for(int j = 1; j <= n; j++) {
if(A.charAt(i - 1) == B.charAt(j - 1)) {
commonSubsequenceLength[i][j] = commonSubsequenceLength[i - 1][j - 1] + 1;
} else {
commonSubsequenceLength[i][j] = Math.max(commonSubsequenceLength[i][j - 1], commonSubsequenceLength[i - 1][j]);
}
}
}
return commonSubsequenceLength[m][n];
}
}
答案 2 :(得分:0)
回答你的问题:
我没有得到的部分是字符串不是以字符串开头的 同一封信,为什么我们采取(s1没有第一个字母,s2),(s1,s2 没有第一个字母)。为什么我们递归地经历这些 他们不匹配时的步骤?它只是一个难以设定的算法 要明白吗?这背后的原因是什么?
令我感到困惑的是递归调用,我猜。整个想法是将问题减少到较小的输入集。在这种情况下,一次少1个字符。 所选角色(第一个或最后一个)有2个案例
有匹配(例如“h”,“空心”,“你好”),只需将两个字符串中的输入大小减少1个字符,然后递归调用相同的函数。
不匹配。这里有2个选项 - 您可以认为第一个字符串有额外的不需要的字符或第二个字符串。因此,对两种情况进行递归调用并选择最大值。
额外详情: 此问题具有典型动态编程(DP)问题的属性。
1)最佳子结构
2)重叠子问题
希望它有所帮助!