【题目描述】
给定两个字符串str1和str2,输出两个字符串的最长公共子串,如果最长公共子串为空,输出-1。
【输入描述】
输入包括两行,第一行代表字符串srr1,第二行代表字符串str2。(1≤length(str1),length(str2)≤5000)
【输出描述】
输出包括一行,代表最长公共子串。
【示例1】
输入
1AB2345CD
12345EF
输出
2345
【备注】
时间复杂度O(n^2),额外空间复杂度O(1)。(n可以为其中任意一个字符串长度)
【代码实现 - CPP版 - 经典动态规划】
#include<iostream>
#include<string>
#include<vector>
using namespace std;
/*
* dp[i][j]表示以stra[i]和strb[j]结尾的字符串的长度
* 第一种情况:stra[i] == strb[j] 公共字符串存在,则有递推公式: dp[i][j] = dp[i-1][j-1] + 1
* 第二种情况:stra[i] != strb[j] 公共字符串不存在,则dp[i][j] = 0
*/
void dp_generator(const string& stra, const string& strb, vector<vector<int>>& dp) {
int lena = stra.length();
int lenb = strb.length();
for(int i=0; i<lena; i++) {
if(stra[i] == strb[0]) {
dp[i][0] = 1;
}
}
for(int j=0; j<lenb; j++) {
if(stra[0] == strb[j]) {
dp[0][j] = 1;
}
}
for(int i=1; i<lena; i++) {
for(int j=1; j<lenb; j++) {
if (stra[i] == strb[j]) {
dp[i][j] = dp[i-1][j-1] + 1;
}
}
}
}
void position_and_maxlen(int& endindex, int& maxlen, const vector<vector<int>>& dp) {
int row = dp.size();
int col = dp[0].size();
for(int i=0; i<row; i++) {
for(int j=0; j<col; j++) {
if(dp[i][j] > maxlen) {
endindex = i;
maxlen = dp[i][j];
}
}
}
}
int main() {
string str_a;
string str_b;
int lena = 0;
int lenb = 0;
cin >> str_a;
cin >> str_b;
lena = str_a.length();
lenb = str_b.length();
// 边界条件
if (lena <=0 || lenb <=0)
return -1;
// 定义并初始化动态规划二维数组
vector<vector<int>> dp(lena, vector<int>(lenb, 0));
dp_generator(str_a, str_b, dp);
// 遍历动态规划数组,获取最大元素
int max_len = 0;
int end_index = 0;
position_and_maxlen(end_index, max_len, dp);
// 最大元素可能为0,反之截取字符串并返回
if (0 == max_len)
cout << -1;
else
cout << str_a.substr(end_index - max_len +1, max_len) << endl;
return 0;
}
【代码实现 - CPP版 - 空间压缩法】
#include<iostream>
#include<string>
using namespace std;
void position_and_maxlen_2(const string& stra, const string& strb, int& maxlen, int& endindex) {
int len_a = stra.size();
int len_b = strb.size();
// 斜线遍历策略的起始行列
int row = 0;
int col = len_b - 1;
// 最大长度以及对应的字符串结尾索引值
maxlen = 0;
endindex = 0;
while (row < len_a) {
int i = row;
int j = col;
int len = 0;
// 遍历以[i,j]为左上起点到右下的斜线
while(i < len_a && j < len_b) {
if(stra[i] != strb[j]) {
len = 0;
} else {
len++;
}
// 记录当前斜线上的最大值,以及结束字符的位置
if(len > maxlen) {
endindex = i;
maxlen = len;
}
// 位置[i,j]计算完成,继续想右下角遍历
i++;
j++;
}
// 当前斜线处理完毕,遍历其左侧斜线
if(col > 0) {
col--;
// 已经完成矩阵对角线斜线的遍历,此时列索引为零。需要向右下方移动,计算新的斜线上所有元素的值
} else {
row++;
}
}
}
int main() {
string str_a;
string str_b;
int lena = 0;
int lenb = 0;
cin >> str_a;
cin >> str_b;
lena = str_a.length();
lenb = str_b.length();
// 边界条件
if (lena <=0 || lenb <=0)
return -1;
int max_len = 0;
int end_index = 0;
position_and_maxlen_2(str_a, str_b, max_len, end_index);
// 最大元素可能为0,反之截取字符串并返回
if (0 == max_len)
cout << -1; // 请勿忽略此类情况
else
cout << str_a.substr(end_index - max_len +1, max_len) << endl;
return 0;
}