动态规划:最长公共子串 & 最长公共子序列

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/afei__/article/details/83153399

一、最长公共子串

1. 题目

给定两个序列 X 和 Y,如果 Z 即是 X 的子串,又是 Y 的子串,我们就称它是 X 和 Y 的公共子串,注意子串是连续的。
例如 X = { A, B, C, D, E, F, G },Y = { A, B, Z, D, E, F, K, G },那么它们最长的公共子串即 { D, E, F }

2. 思路

借助一下《图解算法》中的例子。假设对于两个字符串 fishhish,我们可以绘制一个这样的表格,来求解它们最长的公共子串,即:
图1
注意,最长公共子串的最终答案并不一定在最后一个格子里,所以我们还需要一个变量 max 来记录最大值。

3. 代码

public class Main {
 
    public static void main(String[] args) {
        String s1 = "ABCDEFG";
        String s2 = "ABZDEFKG";
        System.out.println("最长公共子串长度:" + getLCS(s1, s2));
    }
 
    public static int getLCS(String s1, String s2) {
        char[] a = s1.toCharArray();
        char[] b = s2.toCharArray();
        // a.length行,b.length列
        int[][] result = new int[a.length + 1][b.length + 1];
        int max = 0;
        for (int i = 0; i < a.length; i++) {
            for (int j = 0; j < b.length; j++) {
                if (a[i] == b[j]) {
                    result[i + 1][j + 1] = result[i][j] + 1;
                    max = Math.max(max, result[i + 1][j + 1]);
                }
            }
        }
        // ----- print table -----
        System.out.print(" ");
        for (int i = 0; i < b.length; i++) {
            System.out.print(" " + b[i]); // 打印第一行
        }
        System.out.println();
        for (int i = 1; i < result.length; i++) {
            System.out.print(a[i - 1] + " ");
            for (int j = 1; j < result[i].length; j++) {
                System.out.print(result[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println();
        // -----------------------
        return max;
    }
 
}

注意上面代码中 print table 打印部分仅是为了给大家展示表格帮助理解,实际中并不需要。
执行结果:

  A B Z D E F K G
A 1 0 0 0 0 0 0 0 
B 0 2 0 0 0 0 0 0 
C 0 0 0 0 0 0 0 0 
D 0 0 0 1 0 0 0 0 
E 0 0 0 0 2 0 0 0 
F 0 0 0 0 0 3 0 0 
G 0 0 0 0 0 0 0 1 
 
最长公共子串长度:3

如果我们还需要知道具体的最长公共子串是什么,那么就需要再添加一个变量,记录最大值出现的位置,然后往前取最长公共子串的长度即可。

二、最长公共子序列

1. 题目

和最长公共子串类似,只不过子序列是可以不连续的。
例如 X = { A, B, C, D, E, F, G },Y = { A, B, Z, D, E, F, K, G },那么它们最长的公共子串即 { A, B, D, E, F, G }

2. 思路

还是借鉴一下《图解算法》里的例子吧,表格绘制过程如下:
在这里插入图片描述
注意最长公共子序列的话,最大值一定会出现在最后一个格子里。

3. 代码

public class Main {
 
    public static void main(String[] args) {
        String s1 = "ABCDEFG";
        String s2 = "ABZDEFKG";
        System.out.println("最长公共子序列长度:" + getLCS(s1, s2));
    }
 
    public static int getLCS(String s1, String s2) {
        char[] a = s1.toCharArray();
        char[] b = s2.toCharArray();
        // a.length行,b.length列
        int[][] result = new int[a.length + 1][b.length + 1];
        for (int i = 0; i < a.length; i++) {
            for (int j = 0; j < b.length; j++) {
                if (a[i] == b[j]) {
                    result[i + 1][j + 1] = result[i][j] + 1;
                } else {
                    result[i + 1][j + 1] = Math.max(result[i][j + 1], result[i + 1][j]);
                }
            }
        }
        // ----- print table -----
        System.out.print(" ");
        for (int i = 0; i < b.length; i++) {
            System.out.print(" " + b[i]); // 打印第一行
        }
        System.out.println();
        for (int i = 1; i < result.length; i++) {
            System.out.print(a[i - 1] + " ");
            for (int j = 1; j < result[i].length; j++) {
                System.out.print(result[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println();
        // -----------------------
        return result[a.length][b.length];
    }
 
}

注意上面代码中 print table 打印部分仅是为了给大家展示表格帮助理解,实际中并不需要。
执行结果:

  A B Z D E F K G
A 1 1 1 1 1 1 1 1 
B 1 2 2 2 2 2 2 2 
C 1 2 2 2 2 2 2 2 
D 1 2 2 3 3 3 3 3 
E 1 2 2 3 4 4 4 4 
F 1 2 2 3 4 5 5 5 
G 1 2 2 3 4 5 5 6 
 
最长公共子序列长度:6

猜你喜欢

转载自blog.csdn.net/afei__/article/details/83153399