题目描述
给定两个字符串str1 和 str2,输出两个字符串的最长公共子串,保证str1 和str2的最长公共子串存在并且唯一
示例
str1 = "1AB2345CD"
str2 = "12345EF"
substr = "2345"
使用动态规划解决
注意上面描述的是求解最长公共子串,不是最长公共子序列,子序列可以是不连续的内容,但是子串一定是连续的内容。
定义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;
如图所示,有了总结出来的地推公式,代码就比较简单,例如我们使用了两个变量,一个记录最长公共子串,一个即最长公共子串结束的位置。最后将字符串从中截取出来就可以了。
public String LCS(String str1,String str2){
int maxLenth = 0; // 记录最长公共子串的长度
// 记录最长公共子串最后一个元素再字符str1中的位置
int maxLastIndex = 0;
int[][] dp = new int[str1.lenght()+1][str2.lenght()+1]
for(int i = 0;i<str1.length();i++){
for(int j = 0;j<str2.length();j++){
if(str1.charAt(i)==str2.charAt(j)){
dp[i+1][j+1] = dp[i][j]+1;
//如果遇到了更长的子串,要更新,记录最长子串的长度
//以及最长子串最后一个元素的位置
if(dp[i+1][j+1]>maxLenth){
maxLenth = dp[i+1][j+1];
maxLastIndex = i;
}
}else{
// 递推公示两个字符串不相等的情况
dp[i+1][j+1] = 0;
}
}
}
return str1.substring(maxLastIndex-maxLenth+1,maxLastIndex+1)
}
时间复杂度 O(m*n)
空间复杂度 O(m*n)
代码优化,将二维数组变成一维数组。
public String LCS(String str1,String str2){
int maxLenth = 0; // 记录最长公共子串的长度
// 记录最长公共子串最后一个元素再字符str1中的位置
int maxLastIndex = 0;
int[] dp = new int[str2.lenght()+1]
for(int i = 0;i<str1.length();i++){
for(int j = 0;j<str2.length()-1;j++){
if(str1.charAt(i)==str2.charAt(j)){
dp[j+1] = dp[j]+1;
//如果遇到了更长的子串,要更新,记录最长子串的长度
//以及最长子串最后一个元素的位置
if(dp[j+1]>maxLenth){
maxLenth = [j+1];
maxLastIndex = i;
}
}else{
// 递推公示两个字符串不相等的情况
dp[j+1] = 0;
}
}
}
return str1.substring(maxLastIndex-maxLenth+1,maxLastIndex+1)
}
时间复杂度 O(m*n)
空间复杂度 O(n)
总结
这里题目中要求返回的是最长公共子串,需要返回的是一个字符串,还有有些情况下,只需要返回最长公共子串的长度。不过既然能计算出长度,最长公共子串也就可以通过计算得到。希望这里动态规划的解题思路可以为大家提供一定的帮助。