问题描述
给定两个字符串str1和str2,求两个字符的最长公共子串,并且返回最长公共子串。
思路
思路一:定义二维数组dp[i][j] ,dp[i][j]表示,str1以 i 结尾,str2以 j 结尾他们的最长子串的长度是多少,返回最长公共子序列。
思路二:在思路一的基础上我们发现二维数组第一行和第一列的值是可以直接确定的,并且二维数组中任意位置的数字只是依赖它所在从左上到右下直线该点上方的值。有这个发现之后我们就可以使用动态规划的空间压缩技巧进行尝试。
代码
思路一代码
public static String lcst1(String str1,String str2){
if (str1 ==null || str2 ==null ||str1.equals("") || str2.equals("")){
return "";
}
char[] chs1 = str1.toCharArray();
char[] chs2 = str2.toCharArray();
int[][] dp = getdp(chs1,chs2);
int end = 0;
int max = 0;
for (int i = 0;i< chs1.length;i++){
for (int j =0;j<chs2.length;j++){
if (dp[i][j] > max){
end = i;
max=dp[i][j];
}
}
}
return str1.substring(end-max+1,end+1);
}
public static int[][] getdp(char[] str1,char[] str2){
int[][] dp = new int[str1.length][str2.length];
for (int i =0;i<str1.length;i++){
if (str1[i]==str2[0]){
dp[i][0] = 1;
}
}
for (int j =1;j<str2.length;j++){
if (str1[0]==str2[j]){
dp[0][j] = 1;
}
}
for (int i =1;i<str1.length;i++){
for (int j = 1;j<str2.length;j++){
if (str1[i] == str2[i]){
dp[i][j] = dp[i-1][j-1]+1;
}
}
}
return dp;
}
思路二代码
public static String lcst2(String str1,String str2){
if (str1 ==null || str2 ==null ||str1.equals("") || str2.equals("")){
return "";
}
char[] chs1 = str1.toCharArray();
char[] chs2 = str2.toCharArray();
int row = 0;
int col = chs2.length-1;
int max = 0;
int end = 0;
//相当于二维数组从右上角开始向左上角遍历,再从左上角向左下角遍历过程
while (row <chs1.length){
int i = row;
int j = col;
int len = 0;
//数组中任意一个位置上点与之相关的是所在从左上到右下直线上该点所在的上半部分
while (i<chs1.length&& j<chs2.length){
if (chs1[i]!=chs2[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);
}
public static void main(String[] args) {
String str1 = "ABC1234567DEFG";
String str2 = "HIJK1234567MNOP";
System.out.println(lcst1(str1,str2));
System.out.println(lcst2(str1,str2));
}