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);
    }
}
posted @ 2022-02-18 11:12  rananie  阅读(48)  评论(0编辑  收藏  举报