最长公共子字符串

对于两个字符串,请设计一个时间复杂度为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方程是:

matrix[i][j] = { matrix[i-1][j-1]+1, str1[i-1]==str2[j-1] }

 matrix[i][j] = 0, str1[i-1] != str2[j-1]

 最长公共子字符串的长度是矩阵中元素的最大值。

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;  
}  

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值