查找两个字符串a,b中的最长公共子串

查找两个字符串a,b中的最长公共子串

描述
查找两个字符串a,b中的最长公共子串。若有多个,输出在较短串中最先出现的那个。
注:子串的定义:将一个字符串删去前缀和后缀(也可以不删)形成的字符串。请和“子序列”的概念分开!

本题含有多组输入数据!
数据范围:字符串长度,
进阶:时间复杂度:,空间复杂度:
输入描述:
输入两个字符串

输出描述:
返回重复出现的字符
示例1
输入:
abcdefghijklmnop
abcsafjklmnopqrstuvw

输出:
jklmnop

方法一:(暴力枚举)

#include<iostream>
#include<string>
using namespace std;

int main(){
    string str1,str2;
    while(cin>>str1>>str2){
        int n=str1.size(); int m=str2.size();
        bool flag=false;
        if(n<=m){
            for(int len=n;len>0;--len){
                for(int i=0;i<=n-len;++i){
                    for(int j=0;j<=m-len;++j){
                        if(str1.substr(i,len)==str2.substr(j,len)){
                            cout<<str1.substr(i,len)<<endl;
                            flag=true;
                            break;
                        }
                    }
                    if(flag) break;
                }
                if(flag) break;
            }
        }
        else {
            for(int len=m;len>0;--len){
                for(int i=0;i<=m-len;++i){
                    for(int j=0;j<=n-len;++j){
                        if(str2.substr(i,len)==str1.substr(j,len)){
                            cout<<str2.substr(i,len)<<endl;
                            flag=true;
                            break;
                        }
                    }
                    if(flag) break;
                }
                if(flag) break;
            }
        }
    }
    return 0;
}

复杂度分析:

时间复杂度:
O(nm^ 3),其中m是较短字符串的长度,n是较长字符串的长度,枚举s1所有的子串需要O(m^2),find函数查找s2中是否含有子串需要O(mn)
空间复杂度:
O(1),无额外空间

方法二:(优化枚举)

具体做法:

其实找子串不用像方法一一样完全枚举,我们完全可以遍历两个字符串的所有字符串作为起始,然后同时开始检查字符是否相等,相等则不断后移,增加子串长度,如果不等说明以这两个为起点的子串截止了,不会再有了,后续比较长度维护最大值即可。

在这里插入图片描述

#include<iostream>
#include<string>
using namespace std;

int main(){
    string str1,str2;
    while(cin>>str1>>str2){
        int n=str1.length(); int m=str2.length();
        string ans="";
        if(n<=m){
            for(int i=0;i<n;++i){
                for(int j=0;j<m;++j){
                    int len=0;
                    int a=i,b=j;
                    while(str1[a]==str2[b] && a<n && b<m){
                        ++a; ++b; ++len;
                    }
                    if(len>ans.length()){
                        ans=str1.substr(i,len);
                    }
                }
            }
        }
        else {
            for(int i=0;i<m;++i){
                for(int j=0;j<n;++j){
                    int len=0;
                    int b=j,a=i;
                    while(str1[b]==str2[a] && a<m && b<n){
                        ++a; ++b; ++len;
                    }
                    if(len>ans.length()){
                        ans=str2.substr(i,len);
                    }
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

把代码写的更简洁一点:

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

int main(){
    string str1,str2;
    while(cin>>str1>>str2){
        if(str1.length()>str2.length()) swap(str1,str2);
        int n=str1.length(); int m=str2.length();
        string ans="";
        for(int i=0;i<n;++i){
            for(int j=0;j<m;++j){
                int len=0;
                int a=i,b=j;
                while(str1[a]==str2[b] && a<n && b<m){
                    ++a; ++b; ++len;
                }
                if(len>ans.length()){
                    ans=str1.substr(i,len);
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

复杂度分析:

时间复杂度:
O(nm^2),其中m是较短字符串的长度,n是较长字符串的长度,分别枚举两个字符串每个字符作为起点,后续检查子串长度最坏需要花费O(m)
空间复杂度:
O(1),无额外空间

方法三:(动态规划)

具体做法:

动态规划继承自方法二,我们可以用dp[i][j]表示在str1中以第i个字符结尾在str2中以第j个字符结尾时的公共子串长度,遍历两个字符串填充dp数组,在这个过程中比较维护最大值即可。

转移方程为:如果遍历到的该位两个字符相等,则此时长度等于两个前一位长度+1,
dp[i][j]=dp[i−1][j−1]+1,如果遍历到该位时两个字符不相等,则置为0,因为这是子串,必须连续相等,断开要重新开始。

每次更新dp[i][j]后,我们维护最大值即可找到最长子串。

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

int main(){
    string str1,str2;
    while(cin>>str1>>str2){
        if(str1.length()>str2.length()) swap(str1,str2);
        int n=str1.length(); int m=str2.length();
        vector<vector<int>> dp(n+1,vector<int>(m+1,0));
        //dp表示str1以第i个字符结尾的字符串和str2以第j个字符结尾的字符串的公共子字符串长度
    //如果str1的第i个字符和str2的第j个字符相同,那么dp[i][j]=dp[i-1][j-1]+1,否则dp[i][j]置为0
        int maxlen=0,end=0;
        for(int i=1;i<n+1;++i){
            for(int j=1;j<m+1;++j){
                if(str1[i-1]==str2[j-1]){
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else {
                    dp[i][j]=0;
                }
                if(maxlen<dp[i][j]){
                    maxlen=dp[i][j];
                    end=i-1;//记录最长子串结束下标
                }
            }
        }
        cout<<str1.substr(end-maxlen+1,maxlen)<<endl;
    }
    return 0;
}

复杂度分析:

时间复杂度:
O(mn),其中m是较短字符串的长度,n是较长字符串的长度,遍历两个字符串所有字符
空间复杂度:
O(mn),dp数组大小为m∗n

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值