蓝桥杯JAVA-15.LCS最长公共子序列模板(JAVA实现)

个人博客
www.tothefor.com
蓝桥杯复习知识点汇总

纸上得来终觉浅,绝知此事要躬行。路漫漫其修远兮,吾将上下而求索!知识是经过历史的巨人沉淀下来的,别总想着自己能够快速学会,多花点时间去看看,也许会发现些不同的东西。你能快速学会的、觉得简单的东西,对于别人来说也是一样的。人外有人,天外有天。当努力到达了一定的程度,幸运自会与你不期而遇。

目录

求公共子序列长度

状态转移方程:

dp[i][j] = max(dp[i-1][j], dp[i][j-1],dp[i-1][j-1] + (A[i]==B[j] ? 1 : 0)),表示在这三种状态中取到最大值。

(1)第一种状态表示不录入第一个序列的第i个字符时的最长公共子序列,

(2)第二种状态表示不录入第二个序列的第j个字符时的最长公共子序列,

(3)第三种状态表示第一个序列的前i-1个字符与第二个序列前j-1个字符的公共子序列加上最后一个字符的录入状态,如果最后的一个字符相等则录入,状态为1,否则不录入,状态为0。

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * @Author DragonOne
 * @Date 2021/12/5 21:27
 * @墨水记忆 www.tothefor.com
 */
public class Main {
    public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    public static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    public static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    public static Scanner sc = new Scanner(System.in);

    public static int maxd = 10000+7;
    public static int INF = 0x3f3f3f3f;
    public static int mod = 998244353;
    public static int[][] dp = new int[maxd][maxd]; //dp[i][j]表示串a的前i个和串b的前j个的公共子序列的最长长度

    public static void lcs(char[] x,char[] y){
        int lenx = x.length;
        int leny = y.length;
        for(int i=1;i<=lenx;++i){
            for(int j=1;j<=leny;++j){
                if(x[i-1]==y[j-1]) dp[i][j] = dp[i-1][j-1]+1;
                else dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]); //当当前对应位置不等时,就看是不要串a的,还是不要串b中的
            }
        }

    }

    public static void main(String[] args) throws Exception {
        String s1 = nextString();
        String s2 = nextString();
        lcs(s1.toCharArray(),s2.toCharArray());
        System.out.println(dp[s1.length()][s2.length()]);

        closeAll();
    }

    public static void cinInit(){
        cin.wordChars('a', 'z');
        cin.wordChars('A', 'Z');
        cin.wordChars(128 + 32, 255);
        cin.whitespaceChars(0, ' ');
        cin.commentChar('/');
        cin.quoteChar('"');
        cin.quoteChar('\'');
        cin.parseNumbers();
    }
    public static int nextInt() throws Exception {
        cin.nextToken();
        return (int) cin.nval;
    }
    public static long nextLong() throws Exception {
        cin.nextToken();
        return (long) cin.nval;
    }
    public static double nextDouble() throws Exception {
        cin.nextToken();
        return cin.nval;
    }
    public static String nextString() throws Exception {
        cin.nextToken();
        return cin.sval;
    }
    public static void closeAll() throws Exception {
        cout.close();
        in.close();
        out.close();
    }
}

测试数据

输入:

abcfbc abfcab
programming contest 
abcd mnp

输出:

4
2
0

求公共子序列

根据动态规划的状态,来判断我们要求得的序列中的字符有哪些。

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * @Author DragonOne
 * @Date 2021/12/5 21:27
 * @墨水记忆 www.tothefor.com
 */
public class Main {
    public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    public static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    public static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    public static Scanner sc = new Scanner(System.in);

    public static int maxd = 10000+7;
    public static int INF = 0x3f3f3f3f;
    public static int mod = 998244353;
    public static int[][] dp = new int[maxd][maxd]; //dp[i][j]表示串a的前i个和串b的前j个的公共子序列的最长长度

    public static void lcs(char[] x,char[] y){
        int lenx = x.length;
        int leny = y.length;
        for(int i=1;i<=lenx;++i){
            for(int j=1;j<=leny;++j){
                if(x[i-1]==y[j-1]) dp[i][j] = dp[i-1][j-1]+1;
                else dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]); //当当前对应位置不等时,就看是不要串a的,还是不要串b中的
            }
        }
    }
    public static char[] LCSstr(char[] x,char[] y){
        char[] ch = new char[maxd]; //用来存储公共子序列
        int i = x.length;
        int j = y.length;
        int ind = 0; //记录公共子序列在ch数组中的下标,所以最后一定是等于公共子序列的长度的
        while(i!=0 && j!=0){
            if(x[i-1]==y[j-1]){
                ch[ind++]=x[--i]; //因为字符数组是从0开始存储的,所以这里需要先减再存
                j--;
            }else if(dp[i][j-1]>dp[i-1][j]) { //因为前面lcs方法中,要的是dp较大值,所以要的是a串中的,b串中就可以少一个
                j--;
            }else if(dp[i][j-1]<=dp[i-1][j]){ //同理,这里要的是b串中的,所以让a串中少一个
                i--;
            }
        }
        return ch; //存储的最后结果是公共子序列的相反序列,因为是从后往前存的
    }

    public static void main(String[] args) throws Exception {
        String s1 = nextString();
        String s2 = nextString();
        lcs(s1.toCharArray(),s2.toCharArray());

        char[] ans = LCSstr(s1.toCharArray(),s2.toCharArray());
        System.out.println(dp[s1.length()][s2.length()]);

        int rlen = dp[s1.length()][s2.length()];
        for(int i=rlen-1;i>=0;--i){
            System.out.print(ans[i]);
        }
        System.out.println();

        closeAll();
    }

    public static void cinInit(){
        cin.wordChars('a', 'z');
        cin.wordChars('A', 'Z');
        cin.wordChars(128 + 32, 255);
        cin.whitespaceChars(0, ' ');
        cin.commentChar('/');
        cin.quoteChar('"');
        cin.quoteChar('\'');
        cin.parseNumbers();
    }
    public static int nextInt() throws Exception {
        cin.nextToken();
        return (int) cin.nval;
    }
    public static long nextLong() throws Exception {
        cin.nextToken();
        return (long) cin.nval;
    }
    public static double nextDouble() throws Exception {
        cin.nextToken();
        return cin.nval;
    }
    public static String nextString() throws Exception {
        cin.nextToken();
        return cin.sval;
    }
    public static void closeAll() throws Exception {
        cout.close();
        in.close();
        out.close();
    }
}

测试数据

输入:

abcicba
abdkscab

输出:

4
abcb
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值