【题目】
给定两个字符串str1和str2,返回两个字符串的最长公共子串
【举例】
String str1 = “1AB2345CD”;
String str2 = “12345EF”;
输出:2345
【要求】
如果str1长度为M,str2长度为N,实现时间复杂度O(M*N),空间复杂度O(1)
这道题还是用动态规划解。状态转移方程和最长公共子序列有所不同,可以对照着理解。
【常规做法】
先维护一个二维数组,dp,这里规定dp的行数是str2的长度,列数是str1的长度。
dp[i][j]表示以str1[j]当做公共子串最后一个字符,str2[i]也当做公共子串最后一个字符的情况下的最长公共子串。
这样就把大问题化简成了小问题,解空间变小了。因为str1和str2整个的公共子串是一个某个str1中的字符和str2中的字符结尾的,具体是哪个我们不知道。但是通过dp我们相当于遍历了所有的情况。
这个跟数列中求最长上升子序列是一样的。在求最长上升子序列中,最长上升子序列是以哪个值作为子序列的结尾,但是我们可以用dp[i]表示以数列中下标为i的字符结尾的最长上升子序列,最后遍历dp所有的值,哪个值最大就是最长上升子序列了,根据一维数组dp的值可以上升子序列是哪些数字组成。dp[i]的递推公式也就好找了,只要比较当前下标i的数字的大小和下标j(j = i-1,i-2,….0)的数字的大小,如果大,就证明dp[i]能在dp[j]的基础上演化过来。注意这里的初始值是1。因为如果没有比任何一个值大,自身也是上升子序列,结果是1。
回到最长公共子串,递推公式就出来了dp[i][j]初始化为0;
str1[j] == str2[i] dp[i][j] = dp[i-1][j-1] + 1
str1[j] != str2[i] dp[i][j] = 0
下面相当于填写二维表
如下图所示,遍历二维表,找出最大的值即为最大公共子串的长度,所在的行,或者列往前推该长度即可得到字符串结果。
【改进】空间复杂度是O(1)
二维数组的空间复杂度是O(M*N),通过上表填写的过程后,我们可以总结如下规律。dp[i][j]只上它斜上格有关,而且第一行,第一列的值已经可以确定。那我可以斜着一条一条遍历,时间复杂度没有变,因为我还是遍历完了所有的dp。但是dp[i][j]的值我用dp去存储,每次存的都是斜上方的值,后面的是根据前面的dp值做更新,dp值也做更新。设置max值记录出现的最大的dp值,该值即为最长公共子串的长度。这里我用的是两个for循环遍历斜条,开始从dp[str2.length()-1][0],dp[str2.length()-2][0]….dp[0][0]遍历,然后每一个值都往斜下方走。
for (int i = str2.length() -1; i >= 0 ; i--) {
然后遍历dp[0][1]……. dp[0][str1.length()-1],代码如下
for (int j = 1; j < str1.length(); j++) {
代码如下。
public class Main {
public static void main(String[] args) {
String str1 = "1AB2345CD";
String str2 = "12345EF";
System.out.println(lcst(str1, str2));
}
public static String lcst(String str1, String str2){
String res = "";
if (str1 == null || str2 == null || str1.length() == 0 || str1.length() == 0){
return res;
}
int max = -1;
int index_row = -1;
int index_col = -1;
int dp = 0;
for (int i = str2.length() -1; i >= 0 ; i--) {
int row = i, col = 0;
while (row < str2.length() && col < str1.length()){
if (str2.charAt(row) == str1.charAt(col)){
dp += 1;
}
else {
dp = 0;
}
if (max < dp){
max = dp;
index_row = row;
index_col = col;
}
row++;
col++;
}
}
dp = 0;
for (int j = 1; j < str1.length(); j++) {
int row = 0, col = j;
while (row < str2.length() && col < str1.length()){
if (str2.charAt(row) == str1.charAt(col)){
dp += 1;
}
else {
dp = 0;
}
if (max < dp){
max = dp;
index_row = row;
index_col = col;
}
row++;
col++;
}
}
for (int i = 0; i < max; i++) {
res = str1.charAt(index_col) + res;
index_col--;
}
return res;
}
}