问题描述
- 给出两个字符串,找到最长公共子串,并返回其长度。
- Example :
给出A=“ABCD”,B=“CBCE”,返回 2 - 牛客地址
- LintCode地址
问题分析
- 与 最长公共子序列(Longest Common Subsequence)问题 不同,该题是求最长公共子串的长度,子串必须要求连续,而子序列则可以不连续。针对子串这一特点,设置的状态也有所不同。
- 最长公共子序列是属于那种最优子结构问题,而最长公共子串是类似于子数组那种套路问题:先求解以
nums[i]
为开头或者结尾的子数组中的最优,然后遍历所有的nums[i]
,在上述最优中优中选优。
对于该题,我们可以用dp[i][j]
表示 A[i ~ n]
与 B[j ~ m]
中的最长子串的长度,状态转移条件为:如果a[i] == b[j]
,那么dp[i][j] = 1 + dp[i + 1][j + 1]
,如果a[i] != b[j]
,那么dp[i][j] = 0
。
只要我们求出所有的dp[i][j]
,然后max(dp[i][j])
即为所求。
时间复杂度与空间复杂度都是O(N * M)
当然。也可以用用dp[i][j]
表示 A[0 ~i]
与 B[0 ~ j]
中的最长子串的长度 - 该题还可对空间进行优化,因为
dp[i][j]
只依赖于右下方dp[i + 1][j + 1]
,所以我们可以逐条对角线进行更新,只用一个值来记录当前dp[i][j]
的值即可。
空间复杂度:O(1)
经验教训
- 求解最长公共子串与最长公共子序列的区别,如何设置状态才能保证连续。
代码实现
public int longestCommonSubstring(String A, String B) {
if (A == null || B == null) {
return 0;
}
int n = A.length();
int m = B.length();
if (n == 0 || m == 0) {
return 0;
}
int[][] dp = new int[n + 1][m + 1];
int maxLen = 0;
for (int i = n - 1; i >= 0; --i) {
for (int j = 0; j < m; ++j) {
if (A.charAt(i) == B.charAt(j)) {
dp[i][j] = dp[i + 1][j + 1] + 1;
maxLen = Math.max(maxLen, dp[i][j]);
}
}
}
return maxLen;
}
public int longestCommonSubstring(String A, String B) {
if (A == null || B == null) {
return 0;
}
int n = A.length();
int m = B.length();
if (n == 0 || m == 0) {
return 0;
}
int[][] dp = new int[n + 1][m + 1];
int maxLen = 0;
for (int i = n - 1; i >= 0; --i) {
for (int j = 0; j < m; ++j) {
if (A.charAt(i) == B.charAt(j)) {
dp[i][j] = dp[i + 1][j + 1] + 1;
maxLen = Math.max(maxLen, dp[i][j]);
}
}
}
return maxLen;
}