首页 > 试题广场 >

最长公共子串

[编程题]最长公共子串
  • 热度指数:198643 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给定两个字符串str1和str2,输出两个字符串的最长公共子串
题目保证str1和str2的最长公共子串存在且唯一。 

数据范围:
要求: 空间复杂度 ,时间复杂度
示例1

输入

"1AB2345CD","12345EF"

输出

"2345"

备注:
推荐


        如图,自左上->右下的对折线上最长连续交点即为最大字串,为了节省内存开销我没使用二维数组,直接通过记录步长处理的,并在适当位置判断能否提前推出计算,最极限情况,0(2n) 即可退出(str2是str1子串);反之则需要遍历,O(n*m)(最长字串只有一个字符);

        思路其实非常简单,代码看起来方法挺多只是我稍微封装了一下,为了看起来简洁;实际上这种程度的封装,在编译器优化时可以相当轻松的完成方法内联。(评论区贴的不一定是对的,我从评论区拷了很多个拿来测试都是过不了的,一度让我怀疑题目有bug,干了一些蠢事(羞耻到打滚))。

import java.util.*;
public class Solution {
    /**
     * longest common substring
     * @param str1 string字符串 the string
     * @param str2 string字符串 the string
     * @return string字符串
     */
    //状态记录,取缔二维数组,减少内存开销
    int start = 0;
    int stepNum = 0;
    int currentStart = 0;
    int currentStepNum = 0;
    boolean cancleLeft = false;
    boolean cancleRight = false;
    int len2 = 0;
    public String LCS (String str1, String str2) {
        String breakJudge;
        if (str1.length() < str2.length()){
            String temp = str1; str1 = str2; str2 = temp;
        }
        len2 = str2.length();
        for (int i = 0;i < str1.length() && !cancleRight;i++){
            for (int j = 0; j < str2.length() && i + j < str1.length();j++)
                doJudge(str1,str2,j + i,j);
            clear();
            if (!cancleRight)
                cancleRight = breakJudge(str1.length(),i);
        }
        clear();
        for (int i = 0;i < str2.length() && ! cancleLeft;i++){
            for (int j = 0; i + j < str2.length();j++)
                doJudge(str1,str2,j,j + i);
            clear();
            if (!cancleLeft)
                cancleLeft = breakJudge(str2.length(),i);
        }
        return stepNum == 0 ? "-1" : str1.substring(start,start + stepNum);
    }
    // 判断能否提前退出
    public boolean breakJudge(int len,int i){
        if (stepNum == len2 || (stepNum >= (len - i))){
            return true;
        }
        return false;
    }

    public void clear(){
        if (currentStepNum > stepNum){
            stepNum = currentStepNum;
            start = currentStart;
        }
        currentStepNum = 0;
        currentStart = 0;
    }
    public void doJudge(String str1,String str2,int index1,int index2){
        if ( str1.charAt(index1) == str2.charAt(index2)){
            if (currentStepNum == 0)// 记录步长为0
                currentStart = index1; //更新起点
            currentStepNum ++;//步长加一
        } else
            clear();//不等,对比当前步长与缓存步长,更新保存的步长与起点
    }
}
编辑于 2021-07-06 10:32:40 回复(1)
char* LCS(char* s1, char* s2 ) {
    int dp[10000][10000], i, j, maxLen=0, maxS1_i=0;
    char *res;
    res = (char*)malloc((strlen(s1)<strlen(s2)?strlen(s1):strlen(s2))+1);
    strcpy(res, ""); 
    for(i=0; i<=strlen(s1); i++) 
        dp[i][0] = (s2[i]==s1[0] ? 1 : 0);
    for(i=0; i<=strlen(s2); i++) 
        dp[0][i] = (s1[i]==s2[0] ? 1 : 0);
    for(i=1; i<strlen(s1); i++) {
        for(j=1; j<strlen(s2); j++) {
            if(s1[i]==s2[j]) {
                dp[i][j] = dp[i-1][j-1]+1;
                if(maxLen<dp[i][j]) {
                    maxLen = dp[i][j];
                    maxS1_i = i;
                }
            }
            else 
                dp[i][j] = 0;
        }
    }
    strncpy(res, s1+maxS1_i-maxLen+1, maxLen);
    res[maxLen] = 0;
    return res;
}

编辑于 2024-03-25 21:23:18 回复(0)

char* LCS(char* str1, char* str2 ) 
{
    // write code here
    //记录两个字符串长度
    int len1=strlen(str1);
    int len2=strlen(str2);
    //dp用于存储动态规划的结果,即每个公共子串的长度
    int dp[len1+1][len2+1];
    //最长公共子串长度
    int maxLen=0;
    //最长公共子串在str1的结束下标
    int endIndex=0;
    //遍历str1和str2每个元素之间是否存在公共子串,若存在,记录长度
   for(int i=0;i<=len1;i++)
   {
        for(int j=0;j<=len2;j++)
        {
            //第一行和第一列初始化为0,作为边界条件
            if(i==0||j==0)
            {
                dp[i][j]=0;
            }
            else if(str1[i-1]==str2[j-1])//出现公共子串
            {
                //i-1和j-1才是公共子串最后一个元素
                dp[i][j]=dp[i-1][j-1]+1;
                //更新记录最大长度
                if(maxLen<dp[i][j])
                {
                    maxLen=dp[i][j];
                    endIndex=i-1;
                }
            }
            else //没有子串
            {
                dp[i][j]=0;
            }
        }
   }
    //记录子串
    char*res=malloc(maxLen+1);
   if(maxLen==0)
   {
    return NULL;
   }
   else 
   {
        strncpy(res,&str1[endIndex-maxLen+1],maxLen);
        res[maxLen]='\0';    
   }
   return res;
}





发表于 2023-11-22 19:53:21 回复(0)
/*
暴力,双层循环检索是否匹配,匹配则继续下一索引匹配,直到遇到不同字符串。
更新最大公共子串的长度与起始位置。
全部找完后,将最大公共子串复制到返回字符串中。
*/
char* LCS2(char* str1, char* str2 ) {
    // write code here
    char* res = (char*)malloc(sizeof(char)*5000);
    memset(res, 0, sizeof(char)*5000);
    int len1 = strlen(str1), len2 = strlen(str2);
    int n = 0;
    int maxStart = 0, maxN = 0;

    for(int i=0; i<len1; i++){
        for(int j=0; j<len2; j++){
            //注意一定要判断索引是否有效,排查了很久才发现索引超出有效范围。
            while(i+n < len1 && j+n < len2 && str1[i+n] == str2[j+n])n++;  //此处每次都需要遍历,是动规优化的点 
            if(n>maxN){
                maxN = n;
                maxStart = i;
            }
            n=0;
        }
    }
    memcpy(res, &str1[maxStart], sizeof(char)*maxN);

    return res;
}

//动态规划:节省暴力的多次遍历。
//使用上一次的状态即dp[i-1][j-1]来更新当前位置的匹配长度
char* LCS(char* str1, char* str2 ) {
    // write code here
    int maxN = 0, maxEnd = 0;   //此处为maxEnd,即匹配的最后一个字符位置
    char* res = (char*)malloc(sizeof(char)*5000);
    memset(res, 0, sizeof(char)*5000);
    int len1 = strlen(str1), len2 = strlen(str2);
    int dp[len1][len2];
    memset(dp, 0, sizeof(int)*len1*len2);
    for(int i=0; i<len1; i++){
        for(int j=0; j<len2; j++){
            if(str1[i] == str2[j]){ //匹配上
                if(i==0 || j==0){   //如果是边沿,则置1
                    dp[i][j] = 1;
                }else{              //如果不是边沿,则取左上角的dp,来更新最长匹配长度
                    dp[i][j] = dp[i-1][j-1] + 1;
                }
                
            }
            if(maxN < dp[i][j]){    //更新最长字符串的信息
                maxN = dp[i][j];
                maxEnd = i;
            }
        }
    }
    //此处由于是maxEnd,因此需要通过maxEnd-maxN+1来获取匹配字符串的开头位置
    memcpy(res, &str1[maxEnd-maxN+1], sizeof(char)*maxN);
    return res;
}

发表于 2023-03-31 11:16:16 回复(0)
//动态规划
char* LCS(char* str1, char* str2 ) {
    int len1 = strlen(str1);
    int len2 = strlen(str2);
    int maxlen = 0, index;
    int (*dp)[len2] = (int (*)[len2])calloc(len1 * len2, sizeof(int));
    for(int i = 0; i < len1; i++) { //初始化
        if(str2[0] == str1[i]) {
            dp[i][0] = 1;
            maxlen = 1;
            index = i;
        }
    }
    for(int i = 0; i < len2; i++) { //初始化
        if(str1[0] == str2[i]) {
            dp[0][i] = 1;
            maxlen = 1;
            index = 0;
        }
    }
    for(int i = 1; i < len1; i++) {
        for(int j = 1; j < len2; j++) {
            if(str1[i] == str2[j]) {
                dp[i][j] = dp[i-1][j-1] + 1; //状态转移表达式
                if(dp[i][j] > maxlen) {
                    maxlen = dp[i][j];
                    index = i;
                }
            }
        }
    }
    char *str = (char *)calloc(maxlen+1, sizeof(char));
    memcpy(str, str1+(index - maxlen + 1), maxlen); //从str1上拷贝最长公共子串
    return str;
}
发表于 2023-01-08 18:29:06 回复(0)