面试算法题精讲:最长公共子串
最长公共子串问题是指给定两个字符串S1和S2,求它们的公共子串中最长的那一个。其实就是求两个字符串的最长重复子串。
最朴素的算法就是枚举S1和S2的每一对子串,然后判断它们是否相等,时间复杂度是O(n^3)。但是这种算法效率太低,无法满足实际需求。
解法1:动态规划
代码:
int longestCommonSubstring(string s, string t)
{
int n = s.size(), m = t.size();
int maxLen = 0;
// dp[i][j]表示到s的位置i为止、到t的位置j为止、最长的公共子串长度
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
// 状态转移
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
if (s[i - 1] == t[j - 1])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
// 更新最长公共子串的长度
maxLen = max(maxLen, dp[i][j]);
}
else
dp[i][j] = 0; // 不相等,最长公共子串的长度为 0
}
return maxLen;
}
复杂度分析:
时间复杂度:O(nm),其中 m 和 n 分别是字符串 s 和 t 的长度。
空间复杂度:O(nm),其中 m 和 n 分别是字符串 s 和 t 的长度。
实例:
进阶:求最长公共子串的内容
新增一个变量 end 记录最长公共子串在字符串 s 的终点位置,与 maxLen 一起更新即可。
代码:
string longestCommonSubstring(string s, string t)
{
int n = s.size(), m = t.size();
int end = 0, maxLen = 0;
// dp[i][j]表示到s的位置i为止、到t的位置j为止、最长的公共子串长度
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
// 状态转移
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
if (s[i - 1] == t[j - 1])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else
dp[i][j] = 0; // 不相等,最长公共子串的长度为 0
if (dp[i][j] > maxLen)
{ // 更新最长公共子串的长度,和在字符串 s 的终点位置
maxLen = max(maxLen, dp[i][j]);
end = i;
}
}
string lcs = s.substr(end - maxLen, maxLen);
return lcs;
}