最长公共子序列和最长公共子串问题

1,558 阅读3分钟

2019年大疆测试开发岗唯一一道编程题就是最长公共子串问题,看了一下,还有个常见的问题是最长公共子序列问题,今天一并总结一下。

最长公共子序列

1.牛客网地址 [可以全通过]

Ctrl/Cmd + K www.nowcoder.com/practice/47…

2.问题

给定两个字符串str1和str2,返回两个字符串的最长公共子序列,例如:
str1="1A2C3D4B56" ,str2="B1D23CA45B6A"
"123456"和"12C4B6"都是最长公共子序列,返回哪一个都行。
输入:
1A2C3D4B56
B1D23CA45B6A
输出:123456
注:"123456"和“12C4B6”都是最长公共子序列,任意输出一个。

3.代码

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

#define M 10000
#define N 10000
int dp[M][N];

string findTheLongestSameStr(string str1, string str2, int len1, int len2)
{
    string result;
    if (len1 == 0 || len2 == 0)
        return result;
    int m = len1 + 1;
    int n = len2 + 1;

    //初始化
    for (int i = 0; i < m; i++)
        dp[i][0] = 0;
    for (int j = 0; j < n; j++)
        dp[0][j] = 0;
    //填满矩阵
    for (int i = 1; i < m; i++)
        for (int j = 1; j < n; j++)
        {
            if (str1[i - 1] == str2[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    //cout << dp[m - 1][n - 1] << endl;//输出最长公共子序列长度

    //找到最长公共子序列
    for (int i = m - 1, j = n - 1; (i > 0) && (j > 0); )
    {
        if (str1[i - 1] == str2[j - 1])
        {
            result = str1[i - 1] + result;
            i--;
            j--;
        }
        else if (dp[i - 1][j] > dp[i][j - 1])
            i--;
        else
            j--;
    }
    return result;
}

int main()
{
    string str1, str2;
    getline(cin, str1);
    getline(cin, str2);
    int len1 = str1.size();
    int len2 = str2.size();
    string result = findTheLongestSameStr(str1, str2, len1, len2);
    cout << result << endl;
    return 0;
}

最长公共子串

1.牛客网地址[只通过了部分就内存超了]

Ctrl/Cmd + K www.nowcoder.com/practice/21…

2.问题

有两个字符串str和str2,求出两个字符串中最长公共子串长度。
比如:str=acbcbcef,str2=abcbced,则str和str2的最长公共子串为bcbce,最长公共子串长度为5

3.思路

初步:

(1)分别以两个字符为行和列构造矩阵dp[i][j]
(2)若str1[i]==str2[j],dp[i][j]=1,否则为0
(3)通过查找值为1的最长对角线即可找到最长公共子串

Ctrl/Cmd + Shift + I

升级:

(1)将dp规模为len1,len2
(2)若str1[i]==str2[j],dp[i][j]=dp[i-1][j-1]+1,否则为0
(3)dp[i][j]的最大值就是最长公共子序列长度max,最长公共子序列为str2.substr(jmax-max , max)

Ctrl/Cmd + Shift + I

4.代码

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

#define M 100
#define N 100
int dp[M][N];

void findTheMaxSameStr(string str1, string str2)
{
       int len1 = str1.size();
       int len2 = str2.size();
       int m = len1 + 1;
       int n = len2 + 1;
       string result;
       if (len1 == 0 || len2 == 0)
       {
              cout << result <<endl;
              return;
       }
       
       //填充dp矩阵
       int max=0, imax=0, jmax=0;
       for(int i=1; i<m ;i++)
              for (int j = 1; j < n; j++)
              {
                     if (str1[i - 1] == str2[j - 1])
                     {
                           dp[i][j] = dp[i - 1][j - 1] + 1;
                           if (dp[i][j] > max)
                           {
                                  max = dp[i][j];
                                  imax = i;
                                  jmax = j;
                           }
                     }      
              }
              
       cout << max << endl;
       result = str2.substr(jmax-max, max);
       cout << result << endl;
       return;
}

int main()
{
       string str1, str2;
       getline(cin, str1);
       getline(cin, str2);
       memset(dp, 0, sizeof(dp));
       findTheMaxSameStr(str1, str2);
       return 0;
}