NC127 最长公共子串
NC127最长公共子串
题目
描述
给定两个字符串str1和str2,输出两个字符串的最长公共子串
题目保证str1和str2的最长公共子串存在且唯一。
要求:
空间复杂度 O(n^2)
时间复杂度 O(n^2)
示例1
输入:
"1AB2345CD","12345EF"
返回值:
"2345"
备注:
1≤∣str|
∣str| ≤5000
题解-动态规划
看见最长公共子串,首先的反应是动态规划,但是动态规划一般适用于不问具体解只求最优解,这里的返回值让我有点犹豫。
按之前的做题经验来说,这种求解的是最长公共子串的长度,所以也许可以先求解长度和结束位置,就可以返回具体的元素?不知道行不行得通。
定义dp数组
dp[i][j]:表示子串A[i-1]结尾的子串和子串B[j-1]结尾的子串,最长的公共子串长度为dp[i][j]
递归推导式
如果当前A[i-1] = B[i-1]
dp[i][j] = dp[i-1][j-1]+1
如果当前A[i-1] ≠ B[i-1],公共子串要求必须是连续的
dp[i][j] = 0
遍历的顺序与初始化
i=0时与j=0时都需要初始化,不能直接在循环里面计算。
for(int i = 0;i<=lenA;i++){
for(int j = 0;j<=lenB;j++){
if(i==0||j==0){
//初始化代码
dp[i][j] = 0;
continue;
}
if(str1.charAt(i-1)==str2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1]+1;
if(res<dp[i][j]){
end = i-1;//记录结束位置的下标
res = dp[i][j];//记录最长
}
}
}
}
完整代码
import java.util.*;
public class Solution {
public String LCS (String str1, String str2) {
int len1 = str1.length();
int len2 = str2.length();
int res = 0,end=0;
int [][] dp = new int[len1+1][len2+1];
for(int i = 0;i<=len1;i++){
for(int j = 0;j<=len2;j++){
if(i==0||j==0){
//初始化代码
dp[i][j] = 0;
continue;
}
if(str1.charAt(i-1)==str2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1]+1;
if(res<dp[i][j]){
end = i-1;//记录结束位置的下标
res = dp[i][j];//记录最长
}
}
}
} //题目保证存在,且唯一,所以不需要考虑不存在的情况了
return str1.substring(end-res+1,end+1);
}
}
空间优化
如果当前A[i-1] = B[i-1]
dp[i][j] = dp[i-1][j-1]+1
如果当前A[i-1] ≠ B[i-1],公共子串要求必须是连续的
dp[i][j] = 0
可以看出dp[i][j]只与dp[i-1][j-1]有关
滚动数组法:把二维数组压缩成一维数组,我们采用压缩i的方法
如果正向遍历j,dp[j-1]代表的是dp[i][j-1]的值,那是不是可以倒序遍历j?通过举例分析之后,发现是可行的。如果不相等需要赋值0,因为是一维数组会保持前面数组的值。
//初始化的过程删掉了,因为默认是0
public class Solution {
public String LCS (String str1, String str2) {
int len1 = str1.length();
int len2 = str2.length();
int res = 0,end=0;
int [] dp = new int [len2+1];
for(int i = 1;i<=len1;i++){
for(int j = len2;j>0;j--){
if(str1.charAt(i-1)==str2.charAt(j-1)){
dp[j] = dp[j-1]+1;
if(res<dp[j]){
end = i-1;//记录结束位置的下标
res =dp [j];//记录最长
}
}else{
dp[j] = 0;
}
}
} //题目保证存在,且唯一,所以不需要考虑不存在的情况了
return str1.substring(end-res+1,end+1);
}
}