首页 > 试题广场 >

最长公共连续子串

[编程题]最长公共连续子串
  • 热度指数:7108 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
给出两个字符串(可能包含空格),找出其中最长的公共连续子串,输出其长度。

输入描述:
输入为两行字符串(可能包含空格),长度均小于等于50.


输出描述:
输出为一个整数,表示最长公共连续子串的长度。
示例1

输入

abcde
abgde

输出

2
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
	string str1, str2;
	while (getline(cin, str1),getline(cin,str2))
	{
		int max = 0;
		vector<vector<int>> dp(str1.size(),vector<int>(str2.size(),0));
		for (int i = 0; i < str1.size(); i++)
		{
			for (int j = 0; j < str2.size(); 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;					
				if (dp[i][j]>max)
					max = dp[i][j];
			}
		}
		cout << max << endl;
	}
	return 0;
}


发表于 2017-08-31 14:39:25 回复(0)
DP问题,利用空间换时间,时间复杂度O(NM),空间O(NM)
思想:
创建一张二维表,本来这张表是用来存储字符A[i]和B[j]是否相等然后将表中(i,j)位置置为1。
遍历结束后,计算所有的对角线上连续1的个数,取最大值就是结果。但是现在,换种方法,
遍历的同时,计算当前斜对角的值,然后用一个变量res记录最大的值即可。
它的公式为:如果A[i - 1] == B[j - 1],那么dp[i][j] = dp[i - 1][j - 1] + 1;
其中dp[0][...]和dp[...][0]都是0,这是初始状态。
例子:
字符串A:abcde
字符串B:abgde
表1
1   0   0   0   0
0   1   0   0   0
0   0   0   0   0
0   0   0   1   0
0   0   0   0   1
这个不可以直接得到结果,需要再遍历一次计算。
表2
0  0   0   0   0   0
0  1   0   0   0   0
0  0   2   0   0   0
0  0   0   0   0   0
0  0   0   0   1   0
0  0   0   0   0   2
这个可以直接得到结果,不需要再遍历一次计算。

  #include<iostream>
#include<string>
#include<vector>
using namespace std;
int solve(string& A, string& B)
{
int res = 0,n = B.size();
vector<vector<int> > dp(A.size() + 1);
for(int i = 0;i < A.size() + 1;i++)
dp[i].resize(n + 1);
for(int i = 1;i <= A.size();i++)
{
for(int j =  1; j <= n; j++)
{
if(A[i - 1] == B[j - 1])
res = max(res,dp[i][j] = dp[i - 1][j - 1] + 1);
}
}
return res;
}
int main()
{
string A, B;
getline(cin, A);
getline(cin, B);
cout << solve(A, B) << endl;
return0;
}

优化了一下得到空间复杂度为O(n)和O(1)的版本。

 int LongestSubstr(string A,string B)
{     vector<int> dp(A.size() + 1);   
      int res = 0;      for (int i = 1; i <= A.size(); i++)    
      {       
          for (int j = 1; j <= B.size(); j++)        
          {         
              if (A[i - 1] == B[i - 1])      
              res = max(res, dp[j] = dp[j - 1] + 1);       
          }    
       }    
     return res;
}
int LongestSubstr(string A,string B)
{     int res = 0, len = 0, flag = 0;    
      for (int i = 1; i <= A.size(); i++)  
      {     
           for (int j = 1; j <= B.size(); j++)  
           {            
               if (A[i - 1] == B[j - 1])                  
      res = max(res, len += 1), flag = 1;   
           }       
            if (!flag) len = 0;   
           flag = 0;    
     }    
     return res;
}



编辑于 2017-10-14 17:41:54 回复(4)
我提供另一种思路:取较短字符串为move串,较长的是still串。move串从still串上面滑过,他们重叠部分是cover域,比较cover域对应字符。
# coding: utf-8
a, b = raw_input(), raw_input()
if len(a) > len(b):  # 选择比较长的字符串作为still串
    s, m = a, b
else:
    s, m = b, a
len_s, len_m = len(s), len(m)  # still串和move串的长度。

max_len = 0
for i in range(len_m + len_s - 1):
    # 分三种情况确定cover域的范围
    if i < len_m - 1:  # move串没有完全进入still串
        s_range, m_range = (0, i + 1), (len_m - 1 - i, len_m)
    elif len_m-1 <= i <= len_s - 1:  # move串完全进入still串
        s_range, m_range = (i - len_m + 1, i + 1), (0, len_m)
    elif i > len_s - 1:  # move串开始脱出still串
        s_range, m_range = (i - len_m + 1, len_s), (0, len_s + len_m - 1 - i)

    s_cover, m_cover = range(*s_range), range(*m_range)  # still串和move串在cover域内的index

    l = 0
    for j in range(len(s_cover)):
        if s[s_cover[j]] == m[m_cover[j]]:
            l += 1
            if l > max_len:
                max_len = l
        else:
            l = 0
print max_len

发表于 2017-06-26 17:45:19 回复(0)
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str1;
        while((str1 = br.readLine()) != null){
            String str2 = br.readLine();
            System.out.println(solve(str1, str2));
        }
    }
    
    private static int solve(String str1, String str2) {
        int m = str1.length();
        int n = str2.length();
        // dp[i][j]是字符串str1.substring(0, i)和str2.substring(0, j)的最大公共子串的长度
        int[][] dp = new int[m + 1][n + 1];
        int result = 0;
        for(int i = 1; i <= m; i++){
            for(int j = 1; j <= n; j++){
                dp[i][j] = str1.charAt(i - 1) == str2.charAt(j - 1)? dp[i - 1][j - 1] + 1: 0;
                result = Math.max(result, dp[i][j]);
            }
        }
        return result;
    }
}

发表于 2020-10-16 16:04:26 回复(0)
//dp二维转一维,做法类似于第二题思路
import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		String a = sc.nextLine();
		String b = sc.nextLine();
		int[] dp=new int[b.length()+1];
		for(int i=0;i<dp.length;i++) {
			dp[i]=0;
		}
		int res=0,index=1;
		for(int i=0;i<a.length();i++) {
			for(int j=index;j<dp.length;j++) {
				if(a.charAt(i)==b.charAt(j-1)) {
					dp[j]+=dp[j-1]+1;
					res=Math.max(res, dp[j]);
					index=++j;
					break;
				}else{
					for(int k=1;k<b.length();k++) {
						dp[k]=0;
					}
					index=1;
				}
			}
		}
		
		System.out.println(res);
	}
}

发表于 2018-09-07 08:42:10 回复(0)

// 一个串在另一个串上从尾部进入直到串头滑出为止,时间复杂度为O(N^2)的.

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

int main(){
    char str1[51],str2[51];
    int lmax,l;
    while(fgets(str1,51,stdin)!=NULL){
        fgets(str2,51,stdin);
        int l1=0;
        int l2=0;
        while(str1[l1]!='\n'&&str1[l1]!='\0'){l1++;}
        while(str2[l2]!='\n'&&str2[l2]!='\0'){l2++;}
        lmax=0;
        // from front to back
        for(int i=0;i<l1;i++){
            int ii=i;
            int cnt=0;
            int size(i+1,l2);
            l=0;
            for(int j=l2-1;cnt<size;j--,ii--,cnt++){
                if(str1[ii]==str2[j])l+=1;
                else l=0;
                if(l>lmax)lmax=l;
            }
        }
        // from back to front
        for(int i=0;i<l2;i++){
            int ii=i;
            int cnt=0;
            int size(i+1,l1);
            l=0;
            for(int j=l1-1;cnt<size;j--,ii--,cnt++){
                if(str2[ii]==str1[j])l+=1;
                else l=0;
                if(l>lmax)lmax=l;
            }
        }
        // output result
        cout<<lmax<<endl;
    }
    return 0;
}
添加笔记
编辑于 2017-08-24 13:18:48 回复(1)
思路:
准备一个N+1 * N+1大小的二维数组dp,置0。
dp[i][j]代表s1的0-i位置与s2的0-j位置中连续公共子串的最大长度。
则有:
    如果s1[i] == s2[j],dp[i][j] = dp[i-1][j-1] + 1;
    否则,dp[i][j] = 0;
记录最大长度即可。

C++源码:
#include <iostream>
#include <string.h>
using namespace std;

#define MAX_LENGTH 50

int main(){
	string s1, s2;

	getline(cin, s1);
	getline(cin, s2);

	int len1 = s1.size();
	int len2 = s2.size();

	int maxLength = 0;
	int dp[MAX_LENGTH+1][MAX_LENGTH+1];
	memset(dp, 0, sizeof(dp));

	for (int i = 1; i <= len1; i++){
		for (int j = 1; j <= len2; j++){
			if (s1[i-1] == s2[j-1]){
				dp[i][j] = dp[i-1][j-1] + 1;
				if (dp[i][j] > maxLength)
					maxLength = dp[i][j];
			}else{
				dp[i][j] = 0;
			}
		}
	}
	cout << maxLength << endl;
	
	return 0;
}

发表于 2017-07-26 09:50:17 回复(1)
C++:
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
int main(){
	string s1, s2;
	while (getline(cin, s1), getline(cin, s2)){
		int l1 = s1.size();
		int l2 = s2.size();
		vector<vector<int>> dp(l1 + 1, vector<int>(l2 + 1, 0));
		int result = 0;
		for (int i = 1; i <= l1; i++){
			for (int j = 1; j <= l2; j++){
				if (s1[i - 1] == s2[j - 1]){
					dp[i][j] = dp[i - 1][j - 1] + 1;
					result = max(dp[i][j], result);
				}
				else{
					dp[i][j] = 0;
				}
			}
		}
		cout << result << endl;
	}
	return 0;
}
java:
import java.util.*;
public class Main{
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while(sc.hasNext()){
			String s1 = sc.nextLine();
			String s2 = sc.nextLine();
			int l1 = s1.length();
			int l2 = s2.length();
			int result = 0;
			int[][] dp = new int[l1 + 1][l2 + 1];
			for (int i = 1; i <= l1; i++){
				for (int j = 1; j <= l2; j++){
					if (s1.charAt(i - 1) == s2.charAt(j - 1)){
						dp[i][j] = dp[i - 1][j - 1] + 1;
						result = Math.max(dp[i][j], result);
					}
					else{
						dp[i][j] = 0;
					}	
				}
			}
			System.out.println(result);
		}
		sc.close();
	}
}

编辑于 2021-01-25 18:25:43 回复(11)
    var str1 = readline();
    var str2 = readline();
    var arr = [];
    for (var i = 0; i < str1.length; i++) {
        arr[i] = [];
        for (var j = 0; j < str2.length; j++) {
            arr[[i]][j] = 0;
        }
    }
    var max = 0;
    for(var i=0;i<str1.length;i++){
        for (var j = 0; j < str2.length; j++) {
            if (str1[i] == str2[j]) {
                if(i==0 || j==0){
                    arr[i][j] = 1;
                }else{
                    arr[i][j] = arr[i-1][j-1] + 1;
                }
            }
            if(arr[i][j]>max){
                max = arr[i][j];
            }
        }
    }
    print(max);

发表于 2018-10-09 13:03:19 回复(0)
#include<iostream>
#include<string>
using namespace std;
 
#define N 51
int main()
{
    string s1, s2;
    getline(cin, s1);
    getline(cin, s2);
    intdp[N][N]={0};
    if(!s1.length() || !s2.length())
        return -1;
    int maxLength = 0;
    for(int i=0; i<s1.length(); ++i)
    {
        for(int j=0; j<s2.length(); ++j)
        {
            if(s2[j]==s1[i])
                dp[i+1][j+1] = dp[i][j]+1;
            else
                dp[i+1][j+1] = 0;
            if(maxLength<dp[i+1][j+1])
                maxLength = dp[i+1][j+1];
        }
    }
    cout<<maxLength<<endl;
    return 0;
}

发表于 2018-06-12 22:33:19 回复(0)
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
int dp[55][55],Max=0,i,j;
int main(){
    string x,y;
    getline(cin,x);getline(cin,y);
    for(i=1;i<=x.length();i++)
        for(j=1;j<=y.length();j++){
            if(x[i-1]==y[j-1]) dp[i][j]=dp[i-1][j-1]+1;
            else dp[i][j]=0;
            Max=max(Max,dp[i][j]);
        }
    printf("%d\n",Max);
}

发表于 2017-11-10 19:11:40 回复(0)
#按照小的字符串顺序和逆序各遍历一遍取最大值
str1 = raw_input()
str2 = raw_input()
iflen(str1) > len(str2):
    str1,str2 = str2,str1
res = 0
start = 0
fori in range(1,len(str1)+1):
    ifstr1[start:i] in str2:
        res = max(res,i-start)
    else:
        start = i
end = len(str1)
fori in range(len(str1)-1,0,-1):
    ifstr1[i:end] in str2:
        res = max(res,end - i)
    else:
        end = i
print res

发表于 2017-08-30 16:28:13 回复(0)
//经典动态规划问题
importjava.util.Scanner;
publicclassMain{
    publicstaticintLCS(String a,String b){
        intla=a.length();
        intlb=b.length();
        intmax=0;
        int[][]dp=newint[1+la][1+lb];
        for(inti=1;i<=la;i++){
            for(intj=1;j<=lb;j++){
                if(a.charAt(i-1)==b.charAt(j-1)){
                    dp[i][j]=1+dp[i-1][j-1];
                }else{
                    dp[i][j]=0;
                }
                max=Math.max(max,dp[i][j]);
            }
        }
        returnmax;
    }
    publicstaticvoidmain(String[]args){
        Scanner sc=newScanner(System.in);
        String a = sc.nextLine();
        String b = sc.nextLine();
        System.out.println(LCS(a,b));
    }
}
发表于 2017-08-27 22:39:42 回复(0)
动态规划dp i j =dp i-1 j-1 +1
发表于 2021-06-01 09:00:32 回复(0)
一个超级简单超级短的方法并且全过
s1 = input()
s2 = input()
if len(s1) < len(s2):
    ss = s1 # 较短的字符串
    ls = s2 # 较长的字符串
else:
    ss = s2
    ls = s1
re = 0
for i in range(len(ss)):
    j = 0
    while j <= i:
        if ss[j:i + 1] in ls:
            re = max(re, i - j + 1)
        j += 1
print(re)

编辑于 2020-09-26 23:29:48 回复(0)
最长公共自子序列的弱化版

#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <map>
#include <cmath>

using namespace std;


int main(){
    string s1,s2;
    getline(cin,s1);
    getline(cin,s2);
    int dp[55][55];
    memset(dp,0,sizeof(dp));
    int ans=0;
    for (int i = 1; i <= s1.size(); ++i) {
        for (int j = 1; j <= s2.size(); ++j) {
            int a=s1[i-1],b=s2[j-1];
            if(a==b){
                dp[i][j]=dp[i-1][j-1]+1;
                ans=max(ans,dp[i][j]);
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}


发表于 2020-06-18 11:55:53 回复(0)

美团2017 JAVA

[编程题]大富翁游戏
[编程题]拼凑钱币
[编程题]最大矩形面积
[编程题]最长公共连续子串

这道题其实我以前做过,但是这次碰到却一直想不起来,非常难受,交卷后看评论里的动态规划就立马明白了,下面是代码。
stream特性是真的好用,一旦拥有,爱不释手。

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    int max=0;
    public static void main(String[] args){
        Main s=new Main();
        Scanner in=new Scanner(System.in);
        String s1=in.nextLine();
        String s2=in.nextLine();
        int len1=s1.length();
        int len2=s2.length();
        int dp[]=new int[len2+1];
        for (int i=1;i<=len1;i++){
            for(int j=len2;j>=1;j--){
                if (s1.charAt(i-1)==s2.charAt(j-1)){
                    dp[j]=dp[j-1]+1;
                }else{
                    dp[j]=0;
                }
            }
            int cur = Arrays.stream(dp).max().getAsInt();
            if (cur>s.max) s.max=cur;
        }
        System.out.println(s.max);
    }
}
发表于 2020-04-19 17:45:44 回复(0)
O(n)做法,需要先学一下后缀数组,学过后就是一道模板题
将两个字符串串起来,中间以‘{’字符隔开,跑一遍SA
#include<bits/stdc++.h>
using namespace std;
(1154)#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
#define per(i,a,b) for(int i = (int)b;i>=(int)a;i--)
(3710)#define pb push_back
#define mp map_pair
(3711)#define all(x) (x).begin(),(x).end()
#define fi first
(3473)#define se second
#define SZ(x) ((int)(x).size())
const int maxn=10050;
typedef long long ll;
const double pi = acos(-1.0);//pi
const ll mod=1000000007;
const int N=2e5+10,inf=0x3f3f3f3f;
int sa[N];
int rk[N];
int tmp[N];
int lcp[N];
char s[N],t[N];
int n,k;
bool cmp(int i,int j){
    if(rk[i] != rk[j]) return rk[i]<rk[j];
    else
    {
        int ri=i+k<=n?rk[i+k]:-1;
        int rj=j+k<=n?rk[j+k]:-1;
        return ri<rj;
    }
}
void build(char *s,int *sa)
{
    n=strlen(s);
    for(int i=0;i<=n;i++){
        sa[i]=i;
        rk[i]=i<n?s[i]:-1;
    }
    for(k=1;k<=n;k*=2){
        sort(sa,sa+n+1,cmp);
        tmp[sa[0]]=0;
        for(int i=1;i<=n;i++){
            tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1],sa[i])?1:0);
        }
        for(int i=0;i<=n;i++){
            rk[i]=tmp[i];
        }
    }
}
void LCP(char *s,int *sa,int *lcp){
    n=strlen(s);
    for(int i=0;i<=n;i++) rk[sa[i]]=i;
    int h=0;
    lcp[0]=0;
    for(int i=0;i<n;i++){
        int j=sa[rk[i]-1];
        for (h ? h-- : 0; j + h < n&&i + h < n&&s[j + h] == s[i + h]; h++);
        lcp[rk[i]-1] = h;
    }
}
int belong[maxn];
int main(){
    gets(s);
    n=strlen(s);
    for(int i=0;i<n;i++) belong[i]=1;
    s[n++]='{';
    gets(s+n);
   //printf("%s",s);
    int now=n;
    n=strlen(s);
    for(int i=now;i<n;i++) belong[i]=2;
    build(s,sa);
    LCP(s,sa,lcp);
    int ans=0;
    //for(int i=0;i<n;i++) printf("%d ",lcp[i]);
    //printf("\n");
    for(int i=2;i<=n;i++){
        if(belong[sa[i-1]]+belong[sa[i]]==3){
            ans=max(ans,lcp[i-1]);
        }
    }
    printf("%d\n",ans);
    return 0;
}
/*

*/



发表于 2020-03-26 10:27:43 回复(0)
刚开始通过了80%,发现空格也是算的,输入换成getline包含空格就好了~
#include<bits/stdc++.h>
using namespace std;
int res = 0;
int dp(string A, string B){
    int size1 = A.length();
    int size2 = B.length();
    vector<vector<int>> dp(size1+1, vector<int>(size2+1,0));
    for(int i = 1; i <= size1; i++){
        for(int j = 1; j <= size2; j++){
            if(A[i-1] == B[j-1]){
                dp[i][j] = dp[i-1][j-1] + 1;
            }
            if(dp[i][j] != 0 &&dp[i][j] > res)
                res = dp[i][j];
        }
    }
    return res;
}
int main(){
    string A, B;
    while(getline(cin, A), getline(cin, B)){
        int res = dp(A, B);
        cout << res << endl;
    }
    return 0;
}


发表于 2020-03-08 09:46:16 回复(0)
import java.util.Scanner;
 
public class Main {
public static void main(String[] args) {
    Scanner sc=new Scanner(System.in);
    String s1=sc.nextLine();
    String s2=sc.nextLine();
    int max=0;
    for(int i=0;i<s1.length();i++) {
        for(int j=i;j<s1.length();j++){
        String s3=s1.substring(i, j+1);
         
        if(s2.contains(s3)) {max=Math.max(max, s3.length());}
        }
    }
    System.out.println(max);
}
}
只保证一个数组,遍历。通过substring 方法来重复截取,与另一字符串对比,调用contain方法。思路还是比较清晰的
发表于 2019-07-06 16:32:11 回复(0)