题目:给定两个字符串str1和str2,输出两个字符串的最长公共子串,如果最长公共子串为空,输出-1。
输入:"1AB2345CD","12345EF"
输出:"2345"
看完题目想到了用动归,刚开始分析的时候想着,对于每两个元素的比较,有两种情况,1.元素相同,那么当前元素能够成的最长公共子串的长度为上一个的+1;2.元素不相同,那么构成的最长公共子串长度为0。但是没有考虑清楚,对于情况1 的上一个元素是什么样的,动归计算出来的是一个序列还是一个二维矩阵。。。。总之也算是有点进步了,嗯。
在进行最长公共子串判断的时候,由于两个子串中的任何位置都可能存在匹配,因此需要使用两层循环进行遍历,计算出动归矩阵。对于当前位置 [i,j],如果两个子串中 i 和 j 位置的元素对应相等,那么构成一位的公共子串,此时就需要去看看两个子串的对应上个元素是不是相等的,对于 i ,其前面一个元素是 i-1,对于 j ,前面一个元素是 j-1,所以 [i,j] 的值应该为 1+[i-1,j-1],如果当前元素不匹配的话,那么[i,j]直接置为0即可。
在进行计算矩阵的时候,由于字符串的第一个元素没有前置元素,因此可以添加一零行和 一零列来保证第一个元素也有前置状态可循。并且在计算矩阵的过程中,每次遇到对应元素匹配的时候,更新当前的最大长度max和最大长度对应的下标maxIndex,在后面获取最长子串的时候可以直接使用substring函数,根据最大长度和最大的下标确定起始点maxIndex-max+1,确定结束点maxIndex+1进行截取即可。
完整代码如下:
public static String LCS (String str1, String str2) {
// write code here
if(str1==null||str2==null||str1.equals("")||str2.equals("")){
return "-1";
}
int len1 = str1.length()+1;
int len2 = str2.length()+1;
int[][] res = new int[len1][len2];
int max = 0;
int maxIndex = 0;
for(int k=0;k<len2;k++) {
res[0][k]=0;
}
for(int k=0;k<len1;k++) {
res[k][0]=0;
}
for(int i=1;i<len1;i++) {
for(int j=1;j<len2;j++) {
if(str1.charAt(i-1)==str2.charAt(j-1)) {
res[i][j] = res[i-1][j-1]+1;
if(res[i][j]>max) {
max = res[i][j];
maxIndex = i-1;
}
}else {
res[i][j] = 0;
}
}
}
if(max==0){
return "-1";
}else{
return str1.substring(maxIndex-max+1,maxIndex+1);
}
}