首页 > 试题广场 >

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

[编程题]查找两个字符串a,b中的最长公共子串
  • 热度指数:188398 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
查找两个字符串a,b中的最长公共子串。若有多个,输出在较短串中最先出现的那个。
注:子串的定义:将一个字符串删去前缀和后缀(也可以不删)形成的字符串。请和“子序列”的概念分开!

数据范围:字符串长度
进阶:时间复杂度:,空间复杂度:

输入描述:

输入两个字符串



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

输入

abcdefghijklmnop
abcsafjklmnopqrstuvw

输出

jklmnop
//Longest common substring 最长公共子串.子串是连续的。
//和之前的LCS(Longest common subsequence---最长公共子序列)不太一样,子序列不一定是连续的。 
//不连续时,在求出dp矩阵后逆向求lcs时,返回路径既可以按↖方向走,也可以按←或↑走。 
//连续时,逆向求lcs时只能按↖走。不过看题解中,有人直接在求dp时,就限制只能按↘方向动态求dp。学习了~ . 

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

string LCCS(const string& str1, const string& str2) {
	string ret;
	int sz1 = str1.size(), sz2 = str2.size();
	vector<vector<int> > dp(sz1 + 1, vector<int>(sz2 + 1, 0));
	for (int i = 1; i <= sz1; ++i) {
		for (int j = 1; j <= sz2; ++j) {
			if (str1[i - 1] == str2[j - 1]) {
				dp[i][j] = dp[i - 1][j - 1] + 1;
				if (dp[i][j] > ret.size())
					ret = str1.substr(i - dp[i][j], dp[i][j]);
			}
		}
	}
	return ret;
}

int main() {
	string str1, str2;
	while (cin >> str1 >> str2) {
		if (str1.size() > str2.size())
			swap(str1, str2);
		cout << LCCS(str1, str2) << endl;
	}
	return 0;
}

编辑于 2017-08-03 16:14:51 回复(5)
两种做法,第一种懒人做法,第二种dfs
懒人做法:
import java.util.ArrayList;
import java.util.Scanner;

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();
            if (s1.length()<s2.length()) {
                System.out.println(func(s1, s2));
            } else {
                System.out.println(func(s2, s1));
            }
        }
    }

    private static String func(String s1, String s2) {
        ArrayList<String> list = new ArrayList<>();
        for(int i=0;i < s1.length()+1; i++) {
            for(int j=i+1; j<s1.length()+1;j++) {
                String subStr = s1.substring(i ,j);
                if (s2.contains(subStr) && subStr.length()>1) {
                    list.add(subStr);
                }
            }
        }
        int maxLen = 0;
        int index = 0;
        // 找出第一个长度最大的子串索引
        for(int i=0;i<list.size();i++) {
            int len = list.get(i).length();
            if(maxLen < len) {
                maxLen = len;
                index = i;
            }
        }
        return list.get(index);
    }
}
dfs做法:
import java.util.Scanner;

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();
            if (s1.length()<s2.length()) {
                System.out.println(func(s1, s2));
            } else {
                System.out.println(func(s2, s1));
            }
        }
    }

    private static String func(String s1, String s2) {
        // 记录长度
        int[][] dp = new int[s1.length()+1][s2.length()+1];
        int maxLen = 0, startIdx = 0;
        for(int i=0;i<s1.length();i++) {
            for(int j=0;j<s2.length();j++) {
                if (s1.charAt(i) == s2.charAt(j)) {
                    dp[i+1][j+1] = dp[i][j] + 1;
                    if(dp[i+1][j+1] > maxLen) {
                        maxLen = dp[i+1][j+1];
                        startIdx = i - maxLen;
                    }
                }
            }
        }
        return s1.substring(startIdx + 1, startIdx+maxLen+1);
    }
}



发表于 2021-03-26 15:31:54 回复(0)
复杂度n实在想不出,给一个n2的吧
#include <string.h>
int main(){
    char str1[1000],str2[1000];
    int count[1000]; //记录以串1各字符为首字母,后跟最大公共子串长度
    while (~scanf("%s %s",str1,str2)) {
        int max=0;
        char temp[1000];
        memset(count, 0, sizeof(count));
        if (strlen(str1)>strlen(str2)) {
            strcpy(temp, str1);
            strcpy(str1, str2);
            strcpy(str2, temp);
        }
        for (int i=0; i<strlen(str1); i++) { //依次遍历串1中每个字符作为可能出现公共子串的首字母
            for (int j=0; j<strlen(str2); j++) { //遍历找到与串1首字母相同的串2首字母
                if (str1[i]==str2[j]) {
                    int step; //公共子串遍历步长
                    int dist1=(int)strlen(str1)-i,dist2=(int)strlen(str2)-j;
                    for (step=1; step<dist1<dist2?dist1:dist2; step++)
                        if (str1[i+step]!=str2[j+step])
                            break;
                    count[i]=count[i]>step?count[i]:step; //步长数组更新
                    if (i>0 && count[i]>count[max]) {
                        max=i; //位置更新
                    }
                }
            }
        }
        for (int i=max; i<max+count[max]; i++) {
            printf("%c",str1[i]);
        }
        printf("\n");
    }
    return 0;
}

发表于 2020-05-21 11:23:17 回复(0)
while True:
  try:
    a,b = input(),input()
    temp = ""
    count = 0
    #比较字符串长度,将短字符串赋给a
    if len(a) > len(b):
      temp = a
      a = b
      b = temp
    #双循环遍历字符串a
    for i in range(len(a)):
      for j in range(i,len(a)):
        #查找b中公共子字符串
        if a[i:j+1] not in b:
          break
        else:
          #筛选出最长的公共子串
          if count < j-i+1:
            count = j-i+1
            temp = a[i:j+1]
    print(temp)
  except:
    break

发表于 2020-02-14 15:03:45 回复(0)
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <map>
#include <set>
#include <list>
#include <deque>
#include <algorithm>
#include <cctype>
#include <iomanip>
#include<cstdlib>
#include <functional>
#include <bitset>
using namespace std;

void FindCommonSubString()
{
	string str1, str2;
	while (cin >> str1 >> str2)
	{
		if (str1.size() > str2.size())
		{
			string temp = str2;
			str2 = str1;
			str1 = temp;
		}
		int sz1 = str1.size();
		int sz2 = str2.size();
		vector<vector<int>> dp(sz1, vector<int>(sz2, 0));
		for (int i = 0; i < sz1; ++i)
		{
			dp[i][0] = (str1[i] == str2[0] ? 1 : 0);
		}
		for (int i = 0; i < sz2; ++i)
		{
			dp[0][i] = (str2[i] == str1[0] ? 1 : 0);
		}
		for (int i = 1; i < sz1; ++i)
		{
			for (int j = 1; j < sz2; ++j)
			{
				if (str1[i] == str2[j])
				{
					dp[i][j] = dp[i - 1][j - 1] + 1;
				}
			}
		}
		int longest = 0;
		int start1 = 0;
		int start2 = 0;
		for (int i = 0; i < sz1; ++i)
		{
			for (int j = 0; j < sz2; ++j)
			{
				if (dp[i][j]>longest)
				{
					longest = dp[i][j];
					start1 = i - longest + 1;
					start2 = j - longest + 1;
				}
			}
		}
		cout << str1.substr(start1, longest) << endl;
	}
}

int main()
{
	FindCommonSubString();
	return 0;
}


发表于 2016-08-22 11:04:46 回复(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();
        System.out.println(longSubString(str1,str2));
    }
    public static String longSubString(String str1,String str2){
        String longStr = str1.length() > str2.length() ? str1 : str2;
        String shortStr = str1.length() < str2.length() ? str1 : str2;
        
        int shortLen = shortStr.length();
        int longLen = longStr.length();
        
        int maxLen = 0, start = 0;
        
        for(int i = 0; i < shortLen; i++){
            //双指针逐渐靠近
            for(int j = i, k = shortLen; k > j; k--){
                String subStr = shortStr.substring(j,k);
                if(longStr.contains(subStr) && maxLen < subStr.length()){
                    //此时找到一个公共子串
                    //把这个公共子串的长度设置为当前最大的长度
                    maxLen = subStr.length();
                    //把这个公共子串的起始位置设置为切割子串的起始位置
                    start = j;
                    //退出当前循环,进入下一个循环
                }
            }
        }
        return shortStr.substring(start,start+maxLen);
    }
}

发表于 2022-06-23 10:33:48 回复(0)
import java.util.Scanner;

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();
            String longStr = s1.length() > s2.length()? s1 : s2;
            String shortStr = s1.length() < s2.length()? s1 : s2;
            String maxSubStr = shortStr.substring(0,1);
            int maxSubLen = 1;
            for (int i = 1; i <= shortStr.length(); i++) { //每次截取长度为i的字符串  abcde 5
                for (int j = 0; j < shortStr.length()-i+1; j++) {
                    String str = shortStr.substring(j,j+i);//长度为i的字符串
                    if (longStr.contains(str) && str.length() > maxSubLen) {//该串是否在长串里
                        maxSubStr = str;
                        maxSubLen = str.length();
                    }
                }
            }
            System.out.println(maxSubStr);
        }
    }
}

发表于 2021-12-22 14:34:04 回复(1)
#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int main()
{
    string str[2];
    int low,flag;
    while(cin>>str[0]>>str[1])
    {
        flag = 0;
        low=(str[0].size()<str[1].size())?0:1;
        
        for(int i=str[low].size(); i>0; --i)
        {
            if(flag) break;
            for(int j=0; j<(str[low].size()-i+1);++j)
                if(str[(low+1)%2].find(str[low].substr(j,i))!=string::npos)  //fail
                {
                     cout<<str[low].substr(j,i)<<endl;
                     flag=1;
                     break;
                }
                   
        }
    }
    return 0;
}
发表于 2021-10-12 11:09:16 回复(0)
import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str = "";
        while ((str = br.readLine()) != null) {
            String str1 = br.readLine();
            System.out.println(method(str, str1));
        }
        br.close();
    }
    private static String method(String s1, String s2) {
        String shortStr = s1.length() < s2.length() ? s1 : s2;
        String longStr = s1.length() < s2.length() ? s2 : s1;
        for (int i = 0; i < shortStr.length(); i++) {
            for (int j = 0, k = shortStr.length() - i; k <= shortStr.length(); j++, k++) {
                if (longStr.contains(shortStr.substring(j, k))) {
                    return shortStr.substring(j, k);
                }
            }
        }
        return null;
    }
}

发表于 2021-09-27 21:31:00 回复(0)
while True:
    try:
        a,b,res=input(),input(),''
        short,long = (a,b) if len(a)<len(b) else (b,a)
        for i in range(len(short)):
            for j in range(len(short)):
                if short[i:j+1] in long and j+1 -i >len(res):
                    res = short[i:j+1]
        print(res)
    except:
        break

发表于 2021-07-22 11:36:47 回复(0)
跟用动态规划求最长公共子串的长度一样,需要注意以下两点:
(1) dp[i][j]大于当前的最长公共子串长度时才更新,否则会被之后相同长度的子串所替代,不符合题中要求的要最先出现。
(2) 利用当前最长公共子串最后一个字符的位置减去子串的长度,倒推得到子串的起点并记录,以便动规结束后返回完整子串。
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) {
            str1 = str1.trim();
            String str2 = br.readLine().trim();
            // 保证str2比str1长
            if(str2.length() < str1.length()){
                String temp = str1;
                str1 = str2;
                str2 = temp;
            }
            System.out.println(longestSubStr(str1, str2));
        }
    }
    
    private static String longestSubStr(String str1, String str2) {
        int maxLen = 0, start = 0;
        // 动态规划求解,dp[i][j]表示str1[:i-1]和str2[:j-1]的最长公共子串长度
        int[][] dp = new int[str1.length() + 1][str2.length() + 1];
        for(int i = 1; i <= str1.length(); i++){
            for(int j = 1; j <= str2.length(); j++){
                if(str1.charAt(i - 1) == str2.charAt(j - 1)){
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                    if(dp[i][j] > maxLen){
                        maxLen = dp[i][j];       // 更新最大长度
                        start = i - maxLen;      // 推算子串的起始位置
                    }
                }
            }
        }
        return str1.substring(start, start + maxLen);
    }
}
本题数据量比较小,用穷举法也可以AC
while True:
    try:
        s1 = input().strip()
        s2 = input().strip()
        l1, l2 = len(s1), len(s2)
        if l1 < l2:
            s1, s2 = s2, s1
            l1, l2 = l2, l1
        max_len = 0
        res = ""
        for i in range(l2):
            for j in range(i + 1, l2 + 1):
                if s2[i:j] in s1 and j - i > max_len:
                    max_len = j - i
                    res = s2[i:j]
        print(res)
    except:
        break

编辑于 2021-03-30 15:22:54 回复(0)
import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()){
            String[] strings = new String[2];
            strings[0] = scanner.next();
            strings[1] = scanner.next();
            if (strings[0].length() > strings[1].length()){
                String temp = strings[0];
                strings[0] = strings[1];
                strings[1] = temp;
            }
            System.out.println(findLongestCommonSubStr(strings));
        }
    }
    public static String findLongestCommonSubStr(String[] strings){
        int max = strings[0].length();
        while (max >= 1){
            for (int i = 0; i <= strings[0].length()-max; i++){
                for (int j = 0; j <= strings[1].length()-max; j++){
                    if (strings[0].substring(i,i+max).equals(strings[1].substring(j,j+max))){
                        return strings[0].substring(i,i+max);
                    }
                }
            }
            max -= 1;
        }
        return null;
    }
}

发表于 2021-01-28 17:26:43 回复(0)
while True:
    try:
        a, b = input(), input()
        if len(a)>len(b):
            a,b = b, a
        for i in range(len(a),0,-1):
            for j in range(len(a)-i+1):
                if a[j:j+i] in b:
                    print(a[j:j+i])
                    break
            else:
                continue
            break
    except:
        break

发表于 2020-11-06 18:58:07 回复(0)
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string str1,str2;
    while(cin>>str1>>str2)
    {
        //保证str1是比较短的一个串
        if(str1.size()>str2.size())    swap(str1,str2);

        int start = 0;//最长公共字符串的开始位置
        int max = 0;//最长公共字串的长度
        //用短串中的字符去挨个匹配长串中的字符,如果匹配的上,则继续短串和长串往后比,同时len++
        //直到短串中的字符和长串中的字符不一样时,则,记录下最大长度
        for(int i = 0;i<str1.size();i++)
        {
            for(int j = 0;j<str2.size();j++)
            {
                if(str1[i] == str2[j])
                {
                    int tmpi = i;
                    int tmpj = j;
                    int len = 0;
                    while(str1[tmpi++] == str2[tmpj++])     len++;
                    if(max < len)
                    {
                        start = i;
                        max = len;
                    }
                }
            }
        }
        cout<<str1.substr(start,max)<<endl;
    }

    return 0;
}

发表于 2020-07-10 16:01:02 回复(1)
public class Main{
    public static void main(String[] args) {
        java.util.Scanner scanner = new java.util.Scanner(System.in);
        while (scanner.hasNext()) {
            char[] inputCharA = scanner.next().toCharArray();
            char[] inputCharB = scanner.next().toCharArray();
            String result;
            if (inputCharA.length > inputCharB.length) {
                result = find(inputCharB, inputCharA);
            } else {
                result = find(inputCharA, inputCharB);
            }
            System.out.println(result);
        }
    }

    public static String find(char[] inputCharA, char[] inputCharB) {
        int targetStartIndex = -1;
        int targetSubStrLength = -1;
        for (int i = 0; i < inputCharA.length; i++) {
            char charA = inputCharA[i];
            for (int j = 0; j < inputCharB.length; j++) {
                char charB = inputCharB[j];
                if (charA == charB) {
                    int counter = 1;
                    int tmpA = i + 1;
                    for (int h = j + 1; h < inputCharB.length; h++) {
                        if (tmpA < inputCharA.length) {
                            if (inputCharB[h] == inputCharA[tmpA]) {
                                counter++;
                                tmpA++;
                            } else {
                                break;
                            }
                        } else {
                            break;
                        }
                        if (counter > targetSubStrLength) {
                            targetSubStrLength = counter;
                            targetStartIndex = j;
                        }
                    }
                }

            }
        }
        StringBuilder stringBuffer = new StringBuilder();
        for (int i = 0; i < targetSubStrLength; i++) {
            stringBuffer.append(inputCharB[i + targetStartIndex]);
        }
        return stringBuffer.toString();
    }
}

发表于 2020-03-29 19:10:54 回复(0)


import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
            while (sc.hasNext()) 
            { //因为有多组数据,所以要加一个while循环,
               //不然可能会出现:你的输出为: 空.请检查一下你的代码
                String str1 = sc.next();
                String str2 = sc.next();
                String temp;
                if(str1.length() > str2.length())
                {
                    temp = str1;
                    str1 = str2;
                    str2 = temp;
                }
                maxCommonStr(str1, str2);
            }
    }

    private static void maxCommonStr(String str1, String str2) {
        if (str1 == null || str2 == null) {
            System.out.println("-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)
        {
            System.out.println("-1");
        }
        System.out.println(res.toString());

    }
}

发表于 2020-03-27 13:06:54 回复(0)
def longest_public_str(str1,str2):
    if len(str1)>len(str2):
        long_str=str1
        short_str=str2
    else:
        long_str=str2
        short_str=str1
    
    # l 初始化
    l=[]
    for k in range(len(short_str)):
        if short_str[k] in long_str:
            l.append(short_str[k])
            break
    # l=['a']
    
    # 最长子串长度至少为2
    for i in range(len(short_str)):
        s=''
        s+=short_str[i]
        for j in range(i+1,len(short_str)):
            s+=short_str[j]
            if s in long_str and len(s)>len(l[0]):
                l.pop()
                l.append(s)
    return l[0]

while True:
    try:
        str1=input()
        str2=input()
        print(longest_public_str(str1,str2))
    except:
        break

发表于 2020-02-19 21:40:33 回复(0)

c++

从最长长度往下遍历,取较短的那个字符串,去长的里面找,找到了直接退出输出即可

#include <iostream>
#include <string>
using namespace std;
//str1 < str2
string FindPublicStr(string str1, string str2)
{
    int len1 = str1.length(), len2 = str2.length();
    //从大到小遍历子串长度
    for(int l=len1; l>0; --l)
    {
        for(int i=0; i<len1-l+1; ++i)
        {
            int pos = str2.find(str1.substr(i, l));
            if(pos != -1)
                return str1.substr(i, l);
        }
    }
}
int main()
{
    string str1, str2;
    while(cin >> str1 >> str2)
    {
        if(str1.length() < str2.length())
            cout << FindPublicStr(str1, str2) << endl;
        else
            cout << FindPublicStr(str2, str1) << endl;
    }
}

发表于 2020-02-05 22:30:09 回复(0)
while True:
    try:
        a = raw_input()
        b = raw_input()
        if len(a)>len(b):
            a,b = b,a
        ma = len(a)
        mb = len(b)
        ans = ""
        j = 1
        for i in range(ma):
            while j<=ma and a[i:j] in b:
                j += 1
            if (j-1-i)>len(ans):
                ans = a[i:j-1]
            if i == j:
                j += 1
        print ans
    except:
        raise
发表于 2019-07-15 17:48:26 回复(0)
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()) {
            String str1 = sc.next();
            String str2 = sc.next();
            if(str2.length() < str1.length()) {
                String temp = str2;
                str2 = str1;
                str1 = temp;
            }
            int cnt = 0;//存储最长公共子串长度
            String need = "";//存储最长公共子串
            int first = Math.max(str1.length(),str2.length());//存储最长公共字符串出现位置
            for(int i=0; i<str1.length(); i++) {
                for(int j=i+1; j<str1.length(); j++) {
                    String substr = str1.substring(i, j+1);
                    if(str2.contains(substr)) {
                        if(substr.length() > cnt) {
                            cnt = substr.length();
                            need = substr;
                        }
                    }
                }
            }
            System.out.println(need);
        }
    }
}

发表于 2018-10-07 16:21:07 回复(0)