首页 > 试题广场 >

最长公共子串

[编程题]最长公共子串
  • 热度指数:7861 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给定两个字符串str1和str2,输出两个字符串的最长公共子串,如果最长公共子串为空,输出-1。

输入描述:
输入包括两行,第一行代表字符串srr1,第二行代表字符串str2。


输出描述:
输出包括一行,代表最长公共子串。
示例1

输入

1AB2345CD
12345EF

输出

2345

备注:
时间复杂度,额外空间复杂度。(n可以为其中任意一个字符串长度)
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int dp[5010][5010],dpr[5010];
int main(){
    char str1[5010],str2[5010];
    scanf("%s %s",str1+1,str2+1);
    int maxlen=0,maxend=0;
    int len1=strlen(str1+1);
    int len2=strlen(str2+1);
    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;  //dp[i][j]表示以i,j为尾的最长公共子串的长度(连续)
                 if(maxlen<dp[i][j])
                 {
                     maxlen=dp[i][j];
                     maxend=i;
                 }   
            }
        }
    }
    if (maxlen == 0)
    {
        printf("-1");
        return 0;
    }
    for(int i=maxend-maxlen+1;i<=maxend;i++){
        printf("%c",str1[i]);
    }
    return 0;
    //printf("%d",dp[strlen(str1)][strlen(str2)]);
}

编辑于 2020-04-02 22:53:39 回复(0)
#include <bits/stdc++.h>
using namespace std;
int main(){
    string a, b;
    cin>>a>>b;
    int n=a.length(), m=b.length();
    int dp[n+1][m+1], Max=0, l;
    memset(dp, 0, sizeof(dp));
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++){
            if(a[i]==b[j]){
                dp[i+1][j+1] = dp[i][j] + 1;
                if(Max < dp[i+1][j+1]){
                    Max = dp[i+1][j+1];
                    l = i+1;
                }
            }else
                dp[i+1][j+1] = 0;
        }
    if(Max==0)
        cout<<-1<<endl;
    else{
        for(int i=l-Max;i<=l-1;i++)
            cout<<a[i];
    }
    return 0;
}

发表于 2020-02-24 02:18:11 回复(0)
#include<iostream>
#include<string>
using namespace std;

void findLCStr(string A, int n, string B, int m) {
    int c[n+1][m+1];
    int i,j,res=0,res_end;//res 最长公共子串长度,res_end最长公共子串末尾序号
    for(i=0;i<=n;i++) c[i][0]=0;
    for(j=1;j<=m;j++) c[0][j]=0;

    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            if(A[i-1]==B[j-1]){
                c[i][j] = c[i-1][j-1] + 1;
                if(res<c[i][j]){
                    res = c[i][j];
                    res_end = i;
                }
                //res = max(res, c[i][j]);
            }
            else c[i][j] = 0;    //与LCS的区别在这里
        }
    }
    if(res==0) cout<<-1<<endl;
    else{
        for(i=res_end-1-res+1;i<=res_end-1;++i)
            cout<<A[i];
        cout<<endl;
    }
}
int main(){
    string A,B;
    cin>>A>>B;
    findLCStr(A,A.length(),B,B.length());
}

发表于 2020-01-31 13:20:46 回复(3)
#include <iostream>
#include <vector>
#include <string>
#include <stack>//T6
#include <map>

using namespace std;


//最长公共子串,dp二维矩阵,dp[i][j] = dp[i-1][j-1],因为要求连续
//搞清楚dp[i][j]的含义
//注意更新maxLen和maxEnd时,要搞清楚dp[i][j] 表示的是以s1[i],s2[j]“结尾”的公共子串的长度
//因此每一个dp[i][j]都要和maxLen比较一下,然后存起来maxLen和maxEnd
//矩阵右下角的dp[i][j]代表以s1末尾,s2末尾结尾的公共子串的长度,可以为0,而公共子串不一定非要以s1末尾,s2末尾结尾

int main()
{
	string s1, s2;
	cin >> s1 >> s2;
	vector< vector<int>> dp(s1.length(), vector<int>(s2.length(), 0));
	for (int j = 0; j < s2.length(); j++)
		if(s1[0]==s2[j])
			dp[0][j] = 1;
	for (int i = 0; i < s1.length(); i++)
		if (s1[i] == s2[0])
			dp[i][0] = 1;
	int maxLen = 0, maxEnd = 0;
	for (int i = 1; i < s1.length(); i++)
	{
		for (int j = 1; j < s2.length(); j++)
		{
			if (s1[i] == s2[j])
				dp[i][j] = dp[i - 1][j - 1] + 1;
			if (dp[i][j] > maxLen)
			{
				maxLen = dp[i][j];
				maxEnd = i;
			}
		}		
	}
	if (maxLen == 0)
	{
		cout << -1;
		return 0;
	}
	else
	{
		for (int i = maxEnd - maxLen + 1; i <= maxEnd; i++)
			cout << s1[i];
	}
	
	system("pause");
	return 0;
}


发表于 2019-09-30 23:29:49 回复(5)
/**
**		后缀自动机模板题,有兴趣可以学一下
**		可以快速处理一些字符串题 例如
**		10个长度100000串的最长公共子串 
**		出现次数有限制(比如出现次数为k次)的子串 
**      author:XiaKIsGod
**      time:2019.9
**/
#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define pb push_back
#define pii pair<int,int>
#define mk make_pair
#define endl "\n"
#define FIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define FIN freopen("1.txt","r",stdin)
#define all(x) x.begin(),x.end()
#define each(e,v) for(auto e: v)
#define debug(a,n) rep(i,0,n) cout<<a[i]<<" ";cout<<endl;
#define Time cout<<"Time="<<clock()-start_clock<<"ms"<<endl
#define mem(x,v) memset(x,v,sizeof(x))
#define repn(i,a,n) for(int i=a;i<=n;i++)
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
inline int reads(char s[]){char c;int len = 0;while ((c = getchar()) != ' ' && c!='\n') s[len++] = c;s[len] = 0;return len;}
char F[60];template<typename T> inline void write(T x) {if( x < 0 ) putchar( '-' ),x=-x;int cnt = 0 ;while(x) {F[cnt++] = x % 10 + '0' ;x /= 10 ;}while(cnt) putchar(F[--cnt]);putchar(10);}
template<typename T> inline void read(T &ret) {char c;ret = 0;T sgn = 1;do { c = getchar(); } while ((c < '0' || c > '9') && c != '-');if (c == '-') sgn = -1; else ret = c - '0';while ((c = getchar()) >= '0' && c <= '9') ret = ret * 10 + (c - '0'); ret*= sgn;}
template<typename T> inline void _max(T &a,const T b){if(a<b) a = b;}
template<typename T> inline void _min(T &a,const T b){if(a>b) a = b;}
template<typename T> inline void _add(T &a,const T b,const T MOD){a = (a+b)%MOD;}
template<typename T> inline T _mul(T a,T b,const T MOD){T ans = 0;while(b){if(b&1) _add(ans,a,MOD);_add(a,a,MOD);b>>=1;}return ans;}
template<typename T> inline T _pow(T a,T b,const T MOD){T ans = 1;while(b){if(b&1) ans = _mul(ans,a,MOD);a = _mul(a,a,MOD);b>>=1;}return ans;}
using namespace std;
const int N = 5010;
const int sigma_size = 255;
struct Node{
	int ch[sigma_size],len,fa;
}pool[N<<1];
int last = 1,tot = 1;
void extend(int x){
	int p = last;
	int np = last = ++tot;
	pool[np].len = pool[p].len + 1;
	for(;p&&!pool[p].ch[x];p = pool[p].fa) pool[p].ch[x] = np;
	if(!p) pool[np].fa = 1;
	else{
		int q = pool[p].ch[x];
		if(pool[q].len==pool[p].len	+1) pool[np].fa = q;
		else{
			int nq = ++tot;
			pool[nq] = pool[q];
			pool[nq].len = pool[p].len+1;
			pool[np].fa = pool[q].fa = nq;
			for(;p&&pool[p].ch[x]==q;p = pool[p].fa) pool[p].ch[x] = nq;
		} 
	}
}
char s1[N],s2[N];
int main(){
	#ifndef ONLINE_JUDGE
		FIN;
		int start_clock = clock();
	#endif
	char c;
	int len1 = reads(s1);
	int len2 = reads(s2);
	rep(i,0,len1) extend(s1[i]);
	string ans = "";
	int u = 1;string now = "";
	rep(i,0,len2){
		int p = s2[i];
		if(pool[u].ch[p]){
			now += s2[i];
			u = pool[u].ch[p];
			if(now.size()>ans.size()) ans = now;
			continue;
		}
		while(u&&!pool[u].ch[p]) u = pool[u].fa;
		if(!u){
			u = 1;now = "";
		}else{ 
			now = now.substr(now.size()-pool[u].len,pool[u].len);
			now += s2[i];
			u = pool[u].ch[p];
			if(now.size()>ans.size()) ans = now;
		}
	}
	if(ans.size()==0) cout<<-1<<endl;
	else cout<<ans<<endl;
	return 0;
} 

编辑于 2019-10-04 18:16:05 回复(1)
/*
题目要求额外空间复杂度O(1),就不可以用动态规划了
可以将一个串不动,另一个串在此串上向后滑动,每滑动一格扫描一次重叠部分的最长公共子串
然后两个串互换,再滑一次。
最后取最长公共子串。
*/
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String str1 = scanner.nextLine();
        String str2 = scanner.nextLine();
        int size1 = str1.length();
        int size2 = str2.length();
        if (size1 == 0 || size2 == 0) {
            System.out.println("-1");
            return;
        }
        
        // 串2不动,串1向后滑动
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < size2; ++i) {
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < size1 && i + j < size2; ++j) {
                if (str2.charAt(i + j) == str1.charAt(j)) {
                    sb.append(str1.charAt(j));
                } else {
                    result = result.length() > sb.length() ? result : sb;
                    sb = new StringBuilder();
                }
            }
            result = result.length() > sb.length() ? result : sb;
        }
        
        // 串1不动,串2向后滑动
        for (int i = 0; i < size1; ++i) {
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < size2 && i + j < size1; ++j) {
                if (str1.charAt(i + j) == str2.charAt(j)) {
                    sb.append(str2.charAt(j));
                } else {
                    result = result.length() > sb.length() ? result : sb;
                    sb = new StringBuilder();
                }
            }
            result = result.length() > sb.length() ? result : sb;
        }
        if (result.length() == 0) {
            System.out.println("-1");
            return;
        }
        System.out.println(result);
    }
}

发表于 2020-02-24 16:46:30 回复(1)

C++ 代码 时间复杂度O(nm),空间复杂度O(1)

因为计算每一个 的时候只需要计算,所以只要按照斜线方向(遍历 n + m - 1条斜线)计算所有的值,用一个变量维护最大值即可

核心:知道如何遍历 n + m - 1 条斜线(这里从最右上角的斜线开始)

C++ 代码

// 时间复杂度O(nm),空间复杂度O(1)
#include <algorithm>
#include <iostream>

using namespace std;

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    string a, b;
    cin >> a >> b;
    int n = a.size(), m = b.size();

    int len = 0, res = 0;
    int row = 0, col = m - 1; // 从右上角开始

    int lcs_i = 0, lcs_j = 0;

    // 计算矩阵中的每一条斜线上的值
    while (row < n) {
        int i = row, j = col; // 斜线起点
        while (i < n && j < m) { // 遍历当前斜线
            if (a[i] == b[j]) {
                len++;
                if (len > res) {
                    res = len;
                    lcs_i = i, lcs_j = j; // 记录最大长度子串的最后一个元素(i,j)
                }
            } else len = 0; // 断开后,len重新算
            i++, j++;
        }
        len = 0; // 下一条斜线 len 从 0 开始

        // 改变斜线起点
        if (col > 0) col--;
        else row++;
    }

    string s;
    row = lcs_i, col = lcs_j;
    while (row >= 0 && col >= 0) {
        if (a[row] == b[col]) s += a[row];
        else break;
        row--, col--;
    }

    reverse(s.begin(), s.end());

    if (res == 0)
        cout << -1 << endl;
    else
        cout << s << endl;

    return 0;
}
发表于 2020-06-02 15:19:03 回复(0)

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
            String str1 = sc.nextLine().trim();
            String str2 = sc.nextLine().trim();
            System.out.println(maxCommonStr(str1, str2));
        sc.close();
    }

    private static String maxCommonStr(String str1, String str2) {
        if (str1 == null || str2 == null) {
            return "-1";
        }
        int len1 = str1.length();
        int len2 = str2.length();
        int[][] dp = new int[len1 + 1][len2 + 1];
        int lmax = 0;
        int pmax = 0;
        for (int i = 1; i <= len1; i++) {
            for (int j = 1; j <= len2; j++) {
                if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                    if(dp[i][j] > lmax)
                    {
                        lmax = dp[i][j];
                        pmax = i;
                    }

                }
            }
        }
        StringBuffer res = new StringBuffer();
        for (int i = pmax  - lmax  ; i < pmax ; i++) {
            res.append(str1.charAt(i));
        }
        if(lmax == 0)
        {
            return "-1";
        }
        return res.toString();

    }
}
发表于 2020-03-26 22:08:41 回复(0)
import sys

for line in sys.stdin:
    line = line.strip()
    if len(line)<=0:break

    line2 = input().strip()
    if len(line)>len(line2):
        long_str = line
        short_str = line2
    else:
        long_str = line2
        short_str = line
    m,n = len(long_str),len(short_str)
       
    max_len = 0
    max_res = ''
    flag = False
    for left in range(n):
        i,j = 0,left
        if flag:break
        while(i<m):
            if long_str[i]==short_str[j]:
                i+=1
                j+=1
                if j>=n:
                    if j-left+1>max_len:
                        max_len = n-left
                        max_res = short_str[left:]
                        flag = True
                    break
            else:
                if j-left+1>max_len:
                    max_len = j-left+1
                    max_res = short_str[left:j]
                j = left
                i+=1
    print(max_res)
            
        

    

先来一段暴力超时的代码,就是固定left然后往右遍历,去找最长的子串max_res,学到了更厉害的解法就来
发表于 2023-08-03 10:21:16 回复(1)

import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str1 = sc.nextLine();
String str2 = sc.nextLine();
int m = str1.length();
int n = str2.length();
char[] ch1 = str1.toCharArray();
char[] ch2 = str2.toCharArray();
int[][] dp = new int[m + 1][n + 1];
for(int i = 0; i <= m; i++) {
dp[i][0] = 0;
}
for(int j = 0; j <= n; j++) {
dp[0][j] = 0;
}
//二维dp数组,动态规划
int max = 0;
int index = 0;
for(int i = 1; i <= m; i++) {
for(int j = 1; j <= n; j++) {
if(ch1[i - 1] == ch2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}else {
dp[i][j] = 0;
}
if(dp[i][j] > max) {
index = i;
max = dp[i][j];
}
}
}
if(max == 0) {
System.out.println(-1);
}else {
String res = str1.substring(index - max, index);
System.out.println(res);
}
}
}

发表于 2022-04-17 10:09:34 回复(0)
O(MN)解法
#include<bits/stdc++.h>
using namespace std;
int main(){
    string str1,str2;
    cin>>str1>>str2;
    
    int l1=str1.size();
    int l2=str2.size();
    int x,y,length=0;
    vector<vector<int>>dp(l1,vector<int>(l2,0));
    for(int i=0;i<l1;i++){
        for(int j=0;j<l2;j++){
            
            if(str1[i]==str2[j]){
                if(i==0||j==0)dp[i][j]=1;
                else{
                    dp[i][j]=dp[i-1][j-1]+1;
                }
            }
            else dp[i][j]=0;
            if(dp[i][j]>length){
                length=dp[i][j];
                x=i;
            }
        }
    }
    if(length==0)cout<<-1<<endl;
    else{
        string res=str1.substr(x-length+1,length);
        cout<<res<<endl;

    }
}
发表于 2021-01-05 17:16:38 回复(0)
    /**
     * 按对角线走
     * 只记录当前位置的i-1,j-1位置即可
     * @author zms
     * @date
     * @param
     * @return
     */
    public static String maxSeq(String s1, String s2) {
        int length1 = s1.length();
        int length2 = s2.length();
        int max = 0;
        int posJ = 0;
        // 上半个矩形的对角线
        for (int j = length2 - 1; j >= 0; j--) {
            int len = 0;
            int j1 = j;
            for (int i = 0; i < length1; i++) {
                if (j1 >= length2)
                    break;
                if (s1.charAt(i) == s2.charAt(j1)) {
                    len++;
                    if (len > max) {
                        posJ = j1;
                        max = len;
                    }
                } else {
                    len = 0;
                }
                j1++;
            }
        }

        // 下半个矩形的对角线
        for (int i = 1; i < length1; i++) {
            int len = 0;
            int i1 = i;
            for (int j = 0; j < length2; j++) {
                if (i1 >= length1) {
                    break;
                }
                if (s1.charAt(i1) == s2.charAt(j)) {
                    len++;
                    if (len > max) {
                        posJ = j;
                        max = len;
                    }
                } else {
                    len = 0;
                }
                i1++;
            }
        }

        String str = "";
        str = s2.substring(posJ - max+1, posJ+1);
        return str.equals("")?"-1":str;
    }


发表于 2020-07-13 11:29:03 回复(0)
动态规划:时间复杂度M*N,空间M*N。
#include<iostream>
#include<string>
using namespace std;
int main() {
    string s1, s2;
    cin >> s1 >> s2;
    int m = s1.size();
    int n = s2.size();
    if (m == 0 || n == 0) {
        cout << "-1" << endl;
        return 0;
    }
    int dp[m][n];
    int idx_end_in_s1 = 0;
    int max_len = 0;
    for (int i = 0; i < m; i++) {
        if (s1[i] == s2[0]) dp[i][0] = 1;
        else dp[i][0] = 0;
    }
    for (int i = 0; i < n; i++) {
        if (s2[i] == s1[0]) dp[0][i] = 1;
        else dp[0][i] = 0;
    }
    for (int i = 1; i < m; i++) {
        for (int j = 1; j < n; j++) {
            if (s1[i] != s2[j]) dp[i][j] = 0;
            else dp[i][j] = dp[i-1][j-1] + 1;
        }
    }
     
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (dp[i][j] > max_len) {
                max_len = dp[i][j];
                idx_end_in_s1 = i;
            }
        }
    }
    if (max_len == 0) cout << "-1" << endl;
    else cout << s1.substr(idx_end_in_s1 - max_len + 1, max_len);
    return 0;
     
     
}

空间优化:注意上面动态规划只是为了将M*N大小的dp填满,然后找最大的值,最大值可以在计算所有的位置过程中就记录下来。

dp中每个位置的值只与它左上角的值有关,所以可以斜着扫描,只保留一个标量记录左上角位置的值,沿斜着的方向去计算。这样就不需要完全记录dp矩阵,去掉O(MN)空间。
    // 从左上到右下,斜着扫描,计算当s1以i结尾,s2以j结尾时的最长公共子串
    // 每个位置只与它左上角有关,因此,空间复杂度为1,每次斜着一条线扫描时,初始化start为0代表左上角的最长字串
    // 判断当前i,j位置,若s1[i] != s2[j],那么 start置0;若s1[i] == s2[j],那么 start++;
    // 扫描过程中顺便记录最长字串。
#include<iostream>
#include<string>
using namespace std;
int main() {
    string s1, s2;
    cin >> s1 >> s2;
    int m = s1.size();
    int n = s2.size();
    if (m == 0 || n == 0) {
        cout << "-1" << endl;
        return 0;
    }

    // idx_end_in_s1记录公共子串最后一个字符在s1中的位置
    // max_len公共子串长度。这样就能确定字串
    int idx_end_in_s1 = 0;
    int max_len = 0;

    for (int k = 0; k < m; k++) {
        int i = k;
        int j = 0;
        int start = 0;
        while (i >= 0 && i < m && j >= 0 && j < n) {
            if (s1[i] != s2[j]) start = 0;
            else {
                start += 1;
                if (start > max_len) {
                    max_len = start;
                    idx_end_in_s1 = i;
                }
            }
            i++;j++;
        }
    }
    for (int k = 1; k < n; k++) {
        int i = 0;
        int j = k;
        int start = 0;
        while (i >= 0 && i < m && j >= 0 && j < n) {
            if (s1[i] != s2[j]) start = 0;
            else {
                start += 1;
                if (start > max_len) {
                    max_len = start;
                    idx_end_in_s1 = i;
                }
            }
            i++;j++;
        }
    }
    if (max_len == 0) cout << "-1" << endl;
    else cout << s1.substr(idx_end_in_s1 - max_len + 1, max_len);
    return 0;

}


发表于 2020-06-11 21:01:22 回复(0)
#include<bits/stdc++.h>
using namespace std;

int main(){
    string str1;
    string str2;
    cin>>str1;
    cin>>str2;
    int row = 0;
    int col = str2.size()-1;
    int max = 0;
    int end = 0;
    while(row < (int)str1.size()){
        int i = row;
        int j = col;
        int len = 0;
        while(i < (int)str1.size() && j < (int)str2.size()){
            if(str1[i]!=str2[j]){
                len = 0;
            }else{
                len++;
            }
            if(len > max){
                end = i;
                max = len;
            }
            i++;
            j++;
        }
        if(col > 0){
            col--;
        }else{
            row++;
        }
    }
    if (max == 0)
    {
        cout << -1;
        return 0;
    }else{
        for(int i = end-max+1;i < end+1; i++){
            cout<<str1[i];
        }
    }

return 0;

}

编辑于 2020-05-22 18:01:51 回复(0)
想问一下,我写出如下的代码后,为什么在系统提交之后显示“不通过”呢?请哪位高人帮忙指导一下,谢谢!
 
 
package chinasoft;

import java.util.HashSet;
import java.util.Objects;
import java.util.Scanner;
import java.util.Set;

public class TestZiChuan
{
 public static void main(String args[]){
  Scanner sc=new Scanner(System.in);
  String firstStr=sc.next();
  String secondStr=sc.next();
  Set<String> resultSet=new HashSet<String>();
  Set<String> resultFirst=getSonStr(firstStr);
  Set<String> secondFirst=getSonStr(secondStr);
  for(String result1:resultFirst){
   for(String result2:secondFirst){
    if(result1.equals(result2)){
     resultSet.add(result1);
    }
   }
  }
  
  String maxStr="";
  for(String result:resultSet){
   if(result.length()>maxStr.length()){
    maxStr=result;
   }
  }
  if(Objects.isNull(maxStr) || maxStr.equals("")){
   System.out.println("-1");
  }
  else{
   System.out.println(maxStr); 
  }
  sc.close();
 }
 
 public static Set<String> getSonStr(String input){
  Set<String> set=new HashSet<String>();
  int len=input.length();
  String tempStr;
  for(int i=0;i<len;i++){
   tempStr="";
   for(int j=i;j<len;j++){
    tempStr=tempStr+input.charAt(j);
    set.add(tempStr);
   }
  }
  return set;
 }
}

发表于 2019-08-04 08:22:32 回复(1)

问题信息

上传者:小小
难度:
15条回答 7517浏览

热门推荐

通过挑战的用户

查看代码