最长公共子串和最长公共子序列的区别
- 两个字符串的“最值”问题,可以使用二维DP的思路。之前解的最长公共子序列不要求字符之间连续,只需要相对位置不变即可,但是最长公共子串要求所有字符连续。
- 如何在代码中体现字符连续这一逻辑呢?这需要改变二维DP数组的定义,之前的最长公共子序列的二维DP数组存放的是s1字符串以第i个字符结尾的子串和s2字符串以第j个字符结尾的子串的最长公共子序列长度,这次的二维DP数组存放的是s1字符串的第i个字符和s2字符串的第j个字符为最后一个元素所构成的最长公共子串长度。
- 由于s1字符串的第i个字符和s2字符串的第j个字符为最长公共子串的最后一个字符,那么久可以保证前面的字符跟s1字符串的第i个字符和s2字符串的第j个字符是连续的。
最小子问题
当s1和s2两个字符串任意一个字符串长度为0时,即为空字符串时,两者都不可能有共同子串,体现在二维DP表中为i或j为0时DP[i][j]=0,因此初始状态可以视为当s1或者s2长度为0时的状态,此时公共子串长度都为0。
如何思考状态转移方程?
二维DP数组的定义已经变成了存放s1字符串的第i个字符和s2字符串的第j个字符为最后一个元素所构成的最长公共子串长度,则要求dp[i][j],首先要判断这两个字符是否相等:
- 如果s1字符串的第i个字符和s2字符串的第j个字符相等,那么这两个字符包含在公共子串内,此时dp[i][j] = 1+dp[i-1][j-1]。
- 如果s1字符串的第i个字符和s2字符串的第j个字符相等,那么他们就不能构成公共子串,此时dp[i][j] = 0。
如何获得最长公共子串?
在构建dp表的过程中同时记录最长公共子串长度和最长公共子串的结束位置,最后再对字符串进行截取即可。
/*
* @Author: LibraStalker **********
* @Date: 2023-03-26 16:26:51
* @FilePath: BM66-最长公共子串.js
* @Description:
*/
/**
* longest common substring
* @param str1 string字符串 the string
* @param str2 string字符串 the string
* @return string字符串
*/
function LCS( str1 , str2 ) {
// write code here
const str1DPLength = str1.length + 1;
const str2DPLength = str2.length + 1;
const dp = Array.from(new Array(str1DPLength), () => new Array(str2DPLength).fill(0));
let maxCommonLength = 0;
let endIndex = -1;
for (let i = 1; i <= str1.length; ++i) {
for (let j = 1; j <= str2.length; ++j) {
if (str1[i-1] === str2[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
if (maxCommonLength < dp[i][j]) {
maxCommonLength = dp[i][j];
endIndex = i - 1;
}
}
else {
dp[i][j] = 0;
}
}
}
return maxCommonLength === 0 ? "-1" : str1.substring(endIndex-maxCommonLength+1, endIndex+1);
}
module.exports = {
LCS : LCS
};



京公网安备 11010502036488号