首页 > 试题广场 >

最长公共子序列(一)

[编程题]最长公共子序列(一)
  • 热度指数:6759 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给定两个字符串 s1 和 s2,长度为 n 和 m  。求两个字符串最长公共子序列的长度。
所谓子序列,指一个字符串删掉部分字符(也可以不删)形成的字符串。例如:字符串 "arcaea" 的子序列有 "ara" 、 "rcaa" 等。但 "car" 、 "aaae" 则不是它的子序列。
所谓 s1 和 s2 的最长公共子序列,即一个最长的字符串,它既是 s1 的子序列,也是 s2 的子序列。
数据范围 : 。保证字符串中的字符只有小写字母。
要求:空间复杂度 O(n),时间复杂度
进阶:空间复杂度 O(n),时间复杂度

输入描述:
第一行输入一个整数 n 和 m ,表示字符串 s1 和 s2 的长度。
接下来第二行和第三行分别输入一个字符串 s1 和 s2。


输出描述:
输出两个字符串的最长公共子序列的长度
示例1

输入

5 6
abcde
bdcaaa

输出

2

说明

最长公共子序列为 "bc" 或 "bd" ,长度为2    
示例2

输入

3 3
abc
xyz

输出

0
#include <iostream>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    string s1, s2;
    cin >> s1 >> s2;
    // 时间复杂度O(MN),空间复杂度O(MN)
    int dp[n + 1][m + 1];
    for (int i = 0; i <= n; ++i) dp[i][0] = 0;
    for (int j = 0; j <= m; ++j) dp[0][j] = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            dp[i][j] = s1[i - 1] == s2[j - 1] ? 
                       dp[i - 1][j - 1] + 1 : 
                       max(dp[i - 1][j], dp[i][j - 1]);
        }
    }
    cout << dp[n][m];
    return 0;
}

发表于 2022-11-02 14:37:23 回复(0)
#include <bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int dp[N][N]={0};
int n,m;
string a,b;
int main()
{
    cin>>n>>m;
    cin>>a>>b;
    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;
            }else{
                dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]);
            }
        }
    }
    cout<<dp[n][m]<<endl;
    return 0;
}

发表于 2022-03-03 23:39:42 回复(1)
方法1:
dp[i][j]定义为s1[...i]和s2[...j]的最长公共子序列,这里包括索引i和j
dp[i][j]为s1到索引i,s2到索引j的最长公共子序列
n1,n2 = list(map(int,input().split()))
s1 = input()
s2 = input()

def common(s1,s2):
    #dp[i][j]为s1[...i]和s2[...j]的最长公共子序列,这里包括索引i和j
    #dp[i][j]为s1到索引i,s2到索引j的最长公共子序列
    n1 = len(s1)
    n2 = len(s2)
    dp = [[0]*(n2) for _ in range(n1)]
    if s1[0]==s2[0]:
        dp[0][0]=1
    for i in range(n1):
        if s2[0] in s1[:i+1]:
            dp[i][0]=1
    for j in range(n2):
        if s1[0] in s2[:j+1]:
            dp[0][j]=1
    for i in range(1,n1):
        for j in range(1,n2):
            if s1[i]==s2[j]:
                dp[i][j] = dp[i-1][j-1]+1
            else:
                dp[i][j] = max(dp[i-1][j],dp[i][j-1])
    return dp[n1-1][n2-1]

res = common(s1,s2)
print(res)
方法2:
dp[i][j]定义为s1[...i-1]和s2[...j-1]的最长公共子序列,这里包括索引i-1和j-1,不包括索引i和j,即s1[:i],s2[:j]
dp[i][j]为s1截至到第i个字符,s2截至到第j个字符的最长公共子序列
n1,n2 = list(map(int,input().split()))
s1 = input()
s2 = input()

def common(s1,s2):
    #dp[i][j]为s1[...i-1]和s2[...j-1]的最长公共子序列,这里包括索引i-1和j-1,不包括索引i和j,即s1[:i],s2[:j]
    #dp[i][j]为s1截至到第i个字符,s2截至到第j个字符的最长公共子序列
    n1 = len(s1)
    n2 = len(s2)
    dp = [[0]*(n2+1) for _ in range(n1+1)]
    for i in range(1,n1+1):
        for j in range(1,n2+1):
            if s1[i-1]==s2[j-1]:
                dp[i][j] = dp[i-1][j-1]+1
            else:
                dp[i][j] = max(dp[i-1][j],dp[i][j-1])
    return dp[n1][n2]

res = common(s1,s2)
print(res)


发表于 2022-08-29 23:20:36 回复(0)
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        in.nextLine();
        String s1 = in.nextLine();
        String s2 = in.nextLine();

        int dp[][] = new int[n + 1][m + 1];
        for(int i = 0;i <= n;i++){
            for(int j = 0;j <= m;j++){
                if(i == 0 || j == 0){
                    dp[i][j] = 0;
                }else if(s1.charAt(i - 1) == s2.charAt(j - 1)){
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }else{
                    dp[i][j] = Math.max(dp[i][j - 1],dp[i - 1][j]);
                }
            }
        }
        System.out.println(dp[n][m]);
    }
}

发表于 2024-01-31 10:11:45 回复(0)
#include <iostream>
#include <vector>
using namespace std;

int main() {
    string s1,s2;
    int n,m;
    cin >> n >> m;
    cin >> s1 >> s2;
    if (s1.size() > s2.size()) {
        swap(n, m);
        swap(s1,s2);
    }
    vector<int> f(m+1,0);
    int res = 0;
    for (auto i=1; i<=n; i++) {
        auto left_up=0,last=0;
        for (auto j=1; j<=m; j++) {
            auto tmp = left_up;
            left_up = f[j];
            if (s1[i-1] == s2[j-1]) {
                // f[i][j] = f[i-1][j-1] + 1;
                f[j] = tmp + 1;
            }
            else {
                // f[i][j] = max(f[i-1][j],f[i][j-1]);
                f[j] = max(f[j], last);
            }

            last = f[j];
            res = max(res,f[j]);
        }
    }
    cout << res << endl;
}
编辑于 2024-01-19 16:49:29 回复(0)
#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n,m;
    string s1, s2; // s1的长度n < s2的长度m
    cin>>n>>m;
    if(n>m){
        cin>>s1>>s2;
    }else{
        int temp = n;
        n = m;
        m = temp;
        cin>>s2>>s1;
    }
    vector<int> pre(n+1,0);
    vector<int> cur(n+1, 0);
    for(int i=1; i<=m; i++){
        for(int j=1; j<=n; j++){
            if(s1[j-1]==s2[i-1]){
                cur[j] = pre[j-1]+1;
            }else{
                cur[j] = max(cur[j-1], pre[j]);
            }
        }
        pre = cur;
    }
    cout<<pre[n];
    return 0;
}

编辑于 2023-12-27 16:51:11 回复(0)
应该能证明
如果f[m][n]=x
f[m + 1][n + 1]或f[m][n + 1]或f[m + 1][n]最大是x + 1
发表于 2023-09-24 09:45:43 回复(0)
n,m = list(map(int,input().split()))
s1=input()
s2=input()
shorterLength = min(n,m)
longerLength = max(n,m)
# 空间复杂度O(mn)
# dp = [[0]*(longerLength+1) for i in range(shorterLength+1)]
# if shorterLength==m:
#     s1,s2 = s2,s1
# for i in range(1,shorterLength+1):
#     for j in range(1,longerLength+1):
#         if s1[i-1]==s2[j-1]:
#             dp[i][j] = dp[i-1][j-1]+1
#         else:
#             dp[i][j] = max(dp[i-1][j],dp[i][j-1])
# print(dp[shorterLength][longerLength])

# 空间复杂度O(min(m,n))
dp1 = [0]*(shorterLength+1)
dp2 = [0]*(shorterLength+1)
if shorterLength==m:
    s1,s2 = s2,s1
for i in range(1,longerLength+1):
    for j in range(1,shorterLength+1):
        if s2[i-1]==s1[j-1]:
            dp2[j]=dp1[j-1]+1
        else:
            dp2[j]=max(dp2[j-1],dp1[j])
    dp1 = dp2.copy()
print(dp2[shorterLength])

发表于 2023-08-05 22:56:41 回复(0)
#include <iostream>
#include <vector>
//using namespace std;
int longestCommonSubsequence(const std::string& str1,const std::string&str2)
{
    int m=str1.size(),n=str2.size();
    std::vector<std::vector<int>> dp(m+1,std::vector<int>(n+1, 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]=std::max(dp[i-1][j],dp[i][j-1]);
            }
        }
    }
    return dp[m][n];
}

int main() {
    int a, b;
    while (std::cin >> a >> b) // 注意 while 处理多个 case
    { 
        std::string str1, str2;
        std::cin >> str1 >> str2;
        std::cout << longestCommonSubsequence(str1, str2) << std::endl;
    }
}

发表于 2023-07-01 12:55:06 回复(0)
#include <cstdio>
#include <iostream>
using namespace std;

int dp[1001][1001];

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    char s1[n];
    char s2[m];
    scanf("%s%s",s1,s2);
    for(int i = 0; i <= n; ++i){ //前多少个,0~n 共n+1个
        for(int j = 0; j <= m; ++j){ //前多少个,0~m 共m+1个
            if(i==0 || j==0){ 
                dp[i][j] = 0;
                continue;
            }
            //核心4句
            if(s1[i-1] == s2[j-1]){          
                dp[i][j] = dp[i-1][j-1] + 1; 
            }else{  //if(s1[i-1] != s2[j-1])  
                dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
            }
        }
    }
    printf("%d\n",dp[n][m]);
    return 0;
}

发表于 2023-03-16 19:20:30 回复(0)
import sys

i = 0
m = n = 0
s1 = s2 = ""
for line in sys.stdin:
    a = line.split()
    if i == 0:
        m, n = int(a[0]), int(a[1])
    elif i == 1:
        s1 = a[0]
    else:
        s2 = a[0]
    i += 1
dp = [[0 for _ in range(n+1)] for _ in range(m+1)]

for i in range(m):
    for j in range(n):
        if s1[i] == s2[j]:
            dp[i+1][j+1] = dp[i][j] + 1
        else:
            dp[i+1][j+1] = max(dp[i+1][j], dp[i][j+1])
print(dp[m][n])

发表于 2023-03-16 16:50:00 回复(0)
#include "cstdio"
#include "string"
#include "iostream"
using namespace std;
int DP[1001][1001];
int main(){
int m,n;
    scanf("%d%d",&n,&m);
    char a[1001];
    char b[1001];
    scanf("%s%s",a,b);
    for (int i = 0; i <= n; ++i) {
        for (int j = 0; j <= m; ++j) {
            if (i==0||j==0){
                DP[i][j]=0;
                continue;
            }
            if (a[i-1]==b[j-1]){
                DP[i][j]=DP[i-1][j-1]+1;
            } else{
                DP[i][j]= max(DP[i-1][j],DP[i][j-1]);
            }
        }
    }
    printf("%d",DP[n][m]);
}

发表于 2023-03-13 23:08:50 回复(0)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include "cstdio"
using namespace std;
                  /// 集合 : a的前i 个字母,b的前j个字母 中所有公共子序列
                  /// 属性: 长度最大值 max
                  /// 状态 转移:  00 01 10 11 , 代表 a的第i 个字母,b的第j个字母包含不包含在公共子序列中
                  /// 00: f[i-1][j-1]   01 10 不好表示 ,可以用 包含这两个的集合来算 ,因为 f[i][j] 包含 f[i-1][j] 包含 01 , 以此类推10 , 求 max 可以有重复 ,只要不漏掉状态就可以,因为多个重复取最大值 仍然不变
                  /// 且 00 的f[i-1][j-1] 也可以省略 ,因为 f[i-1][j-1] 包含在 f[i][j-1] 和 f[i-1][j] 当中
const int N=1010;
int n,m;
int f[N][N];
char s1[N],s2[N];

int main() {
    cin >> n >> m;
    scanf("%s%s",s1+1,s2+1);

    for(int i=1; i<=n;i++){
        for(int j=1;j<=m;j++){
            f[i][j] = max(f[i-1][j],f[i][j-1]);
            if(s1[i] == s2[j])
            f[i][j] = max (f[i][j] , f[i-1][j-1]+1);

        }

    }


    cout <<f[n][m]<< endl;


    return 0;


   






}
// 64 位输出请用 printf("%lld")

发表于 2023-03-06 08:50:42 回复(0)
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;

void async function () {
    let [n,m]=(await readline()).split(" ").map(it=>parseInt(it));
    let s1=await readline();
    let s2=await readline();
    let mat=new Array(n+1).fill(0).map(()=>new Array(m+1).fill(0));
    for(let i=0;i<n;i++){
        for(let j=0;j<m;j++){
            if(s1[i]==s2[j]){
                mat[i+1][j+1]=mat[i][j]+1;
            }else{
                mat[i+1][j+1]=Math.max(mat[i+1][j],mat[i][j+1]);
            }
        }
    }
    console.log(mat[n][m]);
}()

发表于 2023-01-04 19:33:55 回复(0)
m,n = list(map(int,input().split()))
s = input()
t = input()
def solution(s,t):
    m = len(s)
    n = len(t)
    dp = [[0]*(n+1) for _ in range(m+1)]
    for i in range(1,m+1):
        for j in range(1,n+1):
            if s[i-1] == t[j-1]:
                dp[i][j] = dp[i-1][j-1]+1
            else:
                dp[i][j] = max(dp[i][j-1],dp[i-1][j])
    return dp[m][n]
res = solution(s,t)
print(res)
          
            

发表于 2022-09-08 17:48:39 回复(0)
#首先读入数据
n,m = map(int,input().split(' '))
s1 = input()
s2 = input()
#然后构造一个dp表,构造dp表的时候注意n,m 的位置
dp = [[0 for i in range(m+1)] for j in range(n+1)]
for i in range(1,n+1):
    for j in range(1,m+1):
        if s1[i-1] == s2[j-1]:
            dp[i][j] = dp[i-1][j-1]+1
        else:
            dp[i][j] = max(dp[i-1][j],dp[i][j-1])

print(dp[n][m])


发表于 2022-08-28 17:51:19 回复(0)
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        char[] s1;
        char[] s2;
        int[][] dp;
        s1 = sc.next().toCharArray();
        s2 = sc.next().toCharArray();
        int ans = 0;
        dp = new int[n + 1][m + 1];
//         每个dp[i][j]代表s1以第i个字母结尾,s2以第j个字母结尾时的最长公共子序列数
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m ; j++) {
                if (i == 0 || j == 0) {
                    dp[i][j] = 0;
                    continue;
                }
//                 先假设不相同,则不添加到最长公共子序列中
//                 因此比较s1以第i-1个字母结尾,s2以第j个字母结尾
//                 和s1以第i个字母结尾,s2以第j-1个字母结尾两个谁更大
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
//                 如果相同,则可以添加到最长公共子序列中
//                 因此比较未到达该位置的dp[i - 1][j - 1]+1后和到达该位置的dp[i][j]谁大
                if (s1[i - 1] == s2[j - 1]) {
                    dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] + 1);
//                     dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + 1);
//                     dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j - 1] + 1);
                }
                ans = Math.max(ans, dp[i][j]);
            }
        }
        System.out.println(ans);
    }
}

发表于 2022-05-11 15:48:18 回复(0)
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
    int n,m;
    cin>>n>>m;
    string s1;
    string s2;
    cin>>s1;
    cin>>s2;
    vector<vector<int> > dp(n+1,vector<int>(m+1,0));
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(s1[i]==s2[j])
            {
                dp[i+1][j+1]=dp[i][j]+1;
            }
            else
            {
                dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]);
            }
            
        }
    }
    cout<<dp[n][m]<<endl;
    return 0;
}
    

发表于 2022-04-11 19:25:39 回复(0)
import sys



def longnums(str1):
    if(len(str1) != 0):
        nums1 = str1[1]   
        nums2 = str1[0]  
    len1 = len(nums1)
    len2 = len(nums2)
    dp = [[0 for col in range(len2+1)] for row in range(len1+1)]
    for i in range(1,len1+1):
        for j in range(1,len2+1):
            if(nums1[i-1] == nums2[j-1]):
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i][j-1],dp[i-1][j])
    
    return dp[len1][len2]

str1 = []
for line in sys.stdin:
    a = line.split()
    str1 = a + str1
print(longnums(str1))

发表于 2022-04-06 11:14:21 回复(0)

#include <iostream>

#include <string>

#include <vector>

using namespace std;

class Solution{

    public:

    

    int dp(string s1,string s2){

        //s1是短串

        vector<int >dp(s1.size()+1,0);

        //dp数组

        vector<int >res(s1.size()+1,0);

       //保存上轮dp的结果,当前比较位相等时用来对上一位的最大值+1

        for(int i = 1 ; i <= s2.size();i++){

            for(int j = 1;j <= s1.size();j++){

                //相等时判断上一轮dp上一位的最长序列长

                if(s2[i-1] == s1[j-1]) dp[j] = res[j-1]+1;

                //因为不相等时,可以取当轮dp的最大值,也可是上一轮的本位置dp

                else dp[j] = max(dp[j-1],res[j]);

            }

            //开始下一位的dp时,更新上一轮的dp值进res

            for(int j = 1;j <= s1.size();j++) res[j] = dp[j];

        }

        return dp[s1.size()];

    }

};

int main(){

    int n,m;

    while(cin>>n>>m){

        string s1,s2;

        cin>>s1>>s2;

        if(s1.size() > s2.size()) swap(s1, s2);

//保证s1为短串,dp方法正常进行

        Solution solo;

        cout<<solo.dp(s1,s2)<<endl;

    }

    return 0;

}

发表于 2022-04-03 14:28:39 回复(0)

问题信息

难度:
20条回答 1102浏览

热门推荐

通过挑战的用户

查看代码