一、题目
给定两个字符串s tr1和s tr2,输出两个字符串的最长公共子串 ,
题目保证s tr1和s tr2的最长公共子串存在且唯一。
二、求解思路
请注意,本题要求的是最长公共子串,而非最长公共子序列。子序列允许字符间存在间隔,而子串则必须是连续的字符序列。
我们定义`dp[i][j]`为以字符串`str1`的第`i`个字符和字符串`str2`的第`j`个字符作为末尾元素时所形成的最长公共子串的长度。为了求解`dp[i][j]`,即以`str1`的第`i`个字符和`str2`的第`j`个字符为末尾的最长公共子串,我们首先需要检查这两个字符是否相同。
如果这两个字符不相等,那么它们无法构成公共子串,因此:
`dp[i][j] = 0`;
如果这两个字符相等,我们还需要计算它们前面相等字符的数量,这实际上就是`dp[i-1][j-1]`的值,因此:
`dp[i][j] = dp[i-1][j-1] + 1`。
有了递推公式,代码就比较简单了,我们使用两个变量,一个记录最长的公共子串,一个
记录最长公共子串的结束位置,最后再对字符串进行截取即可,来看下代码
#include <iostream>
#include <vector>
#include <string>
std::string LCS(const std::string& str1, const std::string& str2) {
int maxLength = 0; // 记录最长公共子串的长度
// 记录最长公共子串最后一个元素在字符串str1中的位置
int maxLastIndex = 0;
std::vector<std::vector<int>> dp(str1.length() + 1, std::vector<int>(str2.length() + 1, 0));
for (int i = 0; i < str1.length(); i++) {
for (int j = 0; j < str2.length(); j++) {
// 递推公式,两个字符相等的情况
if (str1[i] == str2[j]) {
dp[i + 1][j + 1] = dp[i][j] + 1;
// 如果遇到了更长的子串,要更新,记录最长子串的长度,
// 以及最长子串最后一个元素的位置
if (dp[i + 1][j + 1] > maxLength) {
maxLength = dp[i + 1][j + 1];
maxLastIndex = i;
}
} else {
// 递推公式,两个字符不相等的情况
dp[i + 1][j + 1] = 0;
}
}
}
// 对字符串进行截取,substring(a, b)中a和b分别表示截取的开始和结束位置
return str1.substr(maxLastIndex - maxLength + 1, maxLength);
}
int main() {
std::string str1 = "abcdef";
std::string str2 = "acbcf";
std::string result = LCS(str1, str2);
std::cout << "Longest Common Substring: " << result << std::endl;
return 0;
}
时间复杂度
:O(m*n),m和n分别表示两个字符串的长度
空间复杂度
:O(m*n)
三、代码实现
代码优化,把二维数组变为一维数组
#include <iostream>
#include <vector>
#include <string>
std::string LCS(const std::string& str1, const std::string& str2) {
int maxLength = 0; // 记录最长公共子串的长度
// 记录最长公共子串最后一个元素在字符串str1中的位置
int maxLastIndex = 0;
std::vector<int> dp(str2.length() + 1, 0);
for (int i = 0; i < str1.length(); i++) {
// 注意这里是倒序
for (int j = str2.length() - 1; j >= 0; j--) {
// 递推公式,两个字符相等的情况
if (str1[i] == str2[j]) {
dp[j + 1] = dp[j] + 1;
// 如果遇到了更长的子串,要更新,记录最长子串的长度,
// 以及最长子串最后一个元素的位置
if (dp[j + 1] > maxLength) {
maxLength = dp[j + 1];
maxLastIndex = i;
}
} else {
// 递推公式,两个字符不相等的情况
dp[j + 1] = 0;
}
}
}
// 对字符串进行截取,substring(a, b)中a和b分别表示截取的开始和结束位置
return str1.substr(maxLastIndex - maxLength + 1, maxLength);
}
int main() {
std::string str1 = "abcdef";
std::string str2 = "acbcf";
std::string result = LCS(str1, str2);
std::cout << "Longest Common Substring: " << result << std::endl;
return 0;
}
时间复杂度
:O(m*n),m和n分别表示两个字符串的长度
空间复杂度
:O(n),只需要一个一维数组即可