对于两个字符串,请设计一个时间复杂度为O(m*n)的算法(这里的m和n为两串的长度),求出两串的最长公共子串的长度。
测试样例:
"1AB2345CD","12345EF"
返回:4
思路:使用动态规划算法,创建二维矩阵:
- 二维矩阵的长宽,分别是2个字符串str1和str2的长度加一;
- 二维矩阵的第0列和第0行,都初始化为0;
matrix[i][j]的定义是:以str1[ i ]和str2[ j ]结尾的2个公共子字符串的长度。如果str1[ i ]不等于str2[ j ],则不是公共子字符串,长度为0。
dp方程是:
最长公共子字符串的长度是矩阵中元素的最大值。
public static int LCS(String str1,String str2){
int n = str1.length();
int m = str2.length();
int matrix_firstDimensionLen = n + 1;
int matrix_secondDimensionLen = m + 1;
int[][] arr = new int[matrix_firstDimensionLen][matrix_secondDimensionLen];
for (int i = 0; i < matrix_firstDimensionLen; i++) {
arr[i][0] = 0;
}
for (int j = 0; j < matrix_secondDimensionLen; j++) {
arr[0][j] = 0;
}
int maxLen = 0 ;
for (int i = 1; i < matrix_firstDimensionLen; i++) {
for (int j = 1; j < matrix_secondDimensionLen; j++) {
if(str1.charAt(i-1) == str2.charAt(j-1)){
arr[i][j] = arr[i-1][j-1] + 1;
maxLen = Math.max(maxLen, arr[i][j]);
}else{
//arr[i][j] = Math.max(arr[i-1][j],arr[i][j-1]);
arr[i][j] = 0;
}
}
}
for (int i = 0; i < matrix_firstDimensionLen; i++) {
for (int j = 0; j < matrix_secondDimensionLen; j++) {
System.out.print(arr[i][j] + "\t");
}
System.out.println("");
}
return maxLen;
}
public static void main(String[] args){
String str = "caba";
String str1 = "bab";
System.out.println(LCS(str,str1));
}
矩阵和最长公共子字符串的长度输出如下:
0 0 0 0
0 0 0 0
0 0 1 0
0 1 0 2
0 0 2 0
2
附上其他大神的解题思路:
/**
找出两个字符串的最长公共连续子串的长度
Longest Common Substring和Longest Common Subsequence是有区别的
X = <a, b, c, f, b, c>
Y = <a, b, f, c, a, b>
X和Y的Longest Common Sequence为<a, b, c, b>,长度为4
X和Y的Longest Common Substring为 <a, b>长度为2
其实Substring问题是Subsequence问题的特殊情况,也是要找两个递增的下标序列
<i1, i2, ...ik> 和 <j1, j2, ..., jk>使
xi1 == yj1
xi2 == yj2
......
xik == yjk
与Subsequence问题不同的是,Substring问题不光要求下标序列是递增的,还要求每次
递增的增量为1, 即两个下标序列为:
<i, i+1, i+2, ..., i+k-1> 和 <j, j+1, j+2, ..., j+k-1>
类比Subquence问题的动态规划解法,Substring也可以用动态规划解决,令
c[i][j]表示Xi和Yi的最大Substring的长度,比如
X = <y, e, d, f>
Y = <y, e, k, f>
c[1][1] = 1
c[2][2] = 2
c[3][3] = 0
c[4][4] = 1
动态转移方程为:
如果xi == yj, 则 c[i][j] = c[i-1][j-1]+1
如果xi ! = yj, 那么c[i][j] = 0
最后求Longest Common Substring的长度等于
max{ c[i][j], 1<=i<=n, 1<=j<=m}
**/
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
int longest_common_substring(char *str1, char *str2)
{
int i,j,k,len1,len2,max,x,y;
len1 = strlen(str1);
len2 = strlen(str2);
int **c = new int*[len1+1];
for(i = 0; i < len1+1; i++)
c[i] = new int[len2+1];
for(i = 0; i < len1+1; i++)
c[i][0]=0; //第0列都初始化为0
for(j = 0; j < len2+1; j++)
c[0][j]=0; //第0行都初始化为0
max = -1;
for(i = 1 ; i < len1+1 ; i++)
{
for(j = 1; j < len2+1; j++)
{
if(str1[i-1]==str2[j-1]) //只需要跟左上方的c[i-1][j-1]比较就可以了
c[i][j]=c[i-1][j-1]+1;
else //不连续的时候还要跟左边的c[i][j-1]、上边的c[i-1][j]值比较,这里不需要
c[i][j]=0;
if(c[i][j]>max)
{
max=c[i][j];
x=i;
y=j;
}
}
}
//输出公共子串
char s[1000];
k=max;
i=x-1,j=y-1;
s[k--]='\0';
while(i>=0 && j>=0)
{
if(str1[i]==str2[j])
{
s[k--]=str1[i];
i--;
j--;
}
else //只要有一个不相等,就说明相等的公共字符断了,不连续了
break;
}
printf("最长公共子串为:");
puts(s);
for(i = 0; i < len1+1; i++) //释放动态申请的二维数组
delete[] c[i];
delete[] c;
return max;
}
int main(void)
{
char str1[1000],str2[1000];
printf("请输入第一个字符串:");
gets(str1);
printf("请输入第二个字符串:");
gets(str2);
int len = longest_common_substring(str1, str2);
printf("最长公共连续子串的长度为:%d\n",len);
system("pause");
return 0;
}