您的位置:首页 > 其它

最长公共子串-不建立dp数组(空间复杂度为O(1))

2016-04-09 16:13 411 查看
题目:给定两个字符串str1,str2,返回两个子字符串的最长公共子串。

例:

输入:str1=”1AB2345CD” str2=”12345EF”,返回”2345”。

这里我不仅不使用dp,并且还要返回路径。

思路:如果我们使用经典动态规划的方法可以做到时间复杂度为O(MXN),额外空间复杂度为O(MXN),经过优化之后的实现可以把额外空间复杂度从O(MXN)降至O(1)。

解法一:动态规划表dp。

生成一个dp[M]
的数组。

dp[i][j]的含义:这里要注意!!!不是在str1[0…i]和str2[0…j]之间的最大公共子串长度!!!。而是我们必须把str1[i]和str2[j]当作最长公共字符串最后一个字符的情况下比如str1=”A1234B”,str2=”CD1234”,dp[3][4]的含义在str1的’3’和str2的’3’当作公共子串最后一个字符的情况下,这种情况就是“123”。所以dp[3][4]为3。再举个例子str1=”A12E4B”,str2=”“CD12F4“,dp[3][4]就是把str1的’E’和str2的’F’当作公共子串最后一个。 这种情况就没有公共子串,所以dp[3][4]为0。介绍了dp[i][j]的意思后,接下来介绍dp[i][j]怎么求。

求法:

dp[i][j]取值有两种情况:

str[i]!=str2[j],这个时候dp[i][j]=0。

str[i]==str2[j],说明str1[i]和[j]可以作为公共子串的最后一个字符,从最后一个字符向左扩大宽度也就得到了dp[i][j]=dp[i-1][j-1]+1。

dp获得了之后,得到最长公共子序列就轻松了。首先找到我们最大的dp是谁然后就把他从那个位置向左裁剪。

代码如下:

public static int[][] getdp(String str1,String str2){
int la=str1.length();
int lb=str2.length();
char[] str1array=str1.toCharArray();
char[] str2array=str2.toCharArray();
int[][] dp=new int[la+1][lb+1];//防止溢出
for (int i = 1; i <=la ; i++) {
for (int j = 1; j <=lb ; j++) {
if (str1array[i-1]==str2array[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
}
}
}
return dp;
}

public static String lcst1(String str1,String str2){
if (str1==null||str2==null||str1.equals("")||str2.equals("")){
return "";
}
int[][]dp=getdp(str1,str2);
int end=0;
int max=0;
for (int i = 1; i <=str1.length() ; i++) {
for (int j = 1; j <=str2.length() ; j++) {
if (dp[i][j]>max){
end=i;
max=dp[i][j];
}
}
}
return str1.substring(end-max,end);
}

public static void main(String[] args) {
String str1="abcde";
String str2="bebcd";
System.out.println(lcst1(str1,str2));
}


解法2:经典动态规划的方法需要大小为MXN的dp矩阵,这里我们可以缩减至O(1),这里我们计算每一个dp[i][j]的时候,最多只需要左上方dp[i-1][j-1]的值,所以按照斜线方向来计算所有的值,只需要一个变量就可以了。而斜线的初始变量为第一行和第一列的初始值。我们这里也会使用Max来记录最大值,并且用end记录最大值出现的末尾。代码如下:

public static void main(String[] args) {
String str1="abcde";
String str2="bebcd";
System.out.println(lcst2(str1, str2));
}

private static String lcst2(String str1, String str2) {
if (str1==null||str2==null||str1.equals("")||str2.equals("")){
return "";
}
int la=str1.length();
int lb=str2.length();
char[] str1array=str1.toCharArray();
char[] str2array=str2.toCharArray();
int row=0;//斜线开始位置的行
int col=lb-1;//斜线开始位置的列
int max=0;//记录最大长度
int end=0;//记录最大长度结尾的位置
while (row<la){
int i=row;
int j=col;
int len=0;//记录初始的值
//从(i,j)向右下方遍历
while (i<la&&j<lb){
if (str1array[i]!=str2array[j]){
len=0;
}else {
len++;
}
//记录最大值,以及结束字符的位置
if (len>max){
end=i;
max=len;
}
i++;
j++;
}
if (col>0){ //斜线左移
col--;
}else { //斜线下移
row++;
}
}
return str1.substring(end-max+1,end+1);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: