最长公共子序列问题——LCS

一、作用
常用于解决字符串的相似度。

二、概念
举个例子,cnblogs这个字符串中子序列有多少个呢?很显然有27个,比如其中的cb,cgs等等都是其子序列,我们可以看出
子序列不见得一定是连续的,连续的那是子串。
我想大家已经了解了子序列的概念,那现在可以延伸到两个字符串了,那么大家能够看出:cnblogs和belong的公共子序列吗?
在你找出的公共子序列中,你能找出最长的公共子序列吗?

这里写图片描述

从图中我们看到了最长公共子序列为blog,仔细想想我们可以发现其实最长公共子序列的个数不是唯一的,可能会有两个以上,
但是长度一定是唯一的,比如这里的最长公共子序列的长度为4。

三、解决方案
步骤一:刻画最长公共子序列递归式
定义X的第i前缀为Xi={x1,x2,x3,…xi}现有两个序列X={x1,x2,x3,…xm},Y={y1,y2,y3,….,yn},设一个C[i,j]: 保存Xi与Yj的LCS的长度。所以得出递归方程为:

这里写图片描述

步骤二:计算LCS长度
步骤三:构造LCS

四、java实现

/**
 * LCS
 * 
 * @author lzn
 *
 */
public class LCS {

    /**
     * 计算最长公共子序列长度
     * 
     * @param X
     * @param Y
     * @return 最长公共子序列长度
     */
    public int LCS_LENGTH(String X, String Y) {

        // 最长公共子序列长度
        int max_length = 0;

        // 字符串X的长度
        int m = X.length();
        // 字符串Y的长度
        int n = Y.length();

        // 数组c[i][j] 用来保存Xi与Yj的LCS的长度
        int[][] c = new int[m + 1][n + 1];
        // 字符串数组b[][] 用来对应计算c[i][j]是所选的子 问题的最优解 当b[i][j]=="left_up"时 表明X[i-1]==Y[j-1]为LCS的一个元素
        String[][] b = new String[m + 1][n + 1];

        // 初始化c[][]为0
        for (int i = 0; i <= m; i++) {
            for (int j = 0; j <= n; j++) {
                c[i][j] = 0;
            }
        }

        // 逐项比较X和Y
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                // 如果X[i]==Y[j] 应该求解X(i-1)和Y(j-1)的LCS
                if (X.charAt(i - 1) == Y.charAt(j - 1)) {
                    c[i][j] = c[i - 1][j - 1] + 1;
                    // 设置b[i][j]为left_up
                    b[i][j] = "left_up";
                } else {
                    // 如果如果X[i]!=Y[j] 应该求解X(i-1)和Y(j)的LCS和X(i)和Y(j-1)的LCS的较长者
                    if (c[i][j - 1] > c[i - 1][j]) {
                        // 若X(i)和Y(j-1)的LCS长度大于X(i-1)和Y(j)的LCS长度
                        c[i][j] = c[i][j - 1];
                        // 设置b[i][j]为up
                        b[i][j] = "up";
                    } else {
                        // 若X(i-1)和Y(j)的LCS长度大于X(i)和Y(j-1)的LCS长度
                        c[i][j] = c[i - 1][j];
                        // 设置b[i][j]为left
                        b[i][j] = "left";
                    }
                }

                max_length = c[i][j];
            }
        }

        // 打印结果
        System.out.print("LCS: ");
        PRINT_LCS(b, X, m, n);
        System.out.println();

        // 返回最长公共子序列长度
        return max_length;
    }

    /**
     * 构造LCS(逆序)
     * 
     * @param b     字符串数组b[][] 用来对应计算c[i][j]是所选的子问题的最优解
     * @param X     字符串X
     * @param i     字符串X的长度
     * @param j     字符串Y的长度
     */
    public void PRINT_LCS(String[][] b, String X, int i, int j) {

        if (i == 0 || j == 0) {
            return;
        }

        if ("left_up".equals(b[i][j])) {
            System.out.print(X.charAt(i - 1));
            PRINT_LCS(b, X, i - 1, j - 1);
        } else {
            if ("up".equals(b[i][j])) {
                PRINT_LCS(b, X, i, j - 1);
            } else if ("left".equals(b[i][j])) {
                PRINT_LCS(b, X, i - 1, j);
            }
        }
    }

    public static void main(String[] args) {
        String a = "cnblogslfszcn";
        String b = "belongaldfzng";

        int length = new LCS().LCS_LENGTH(a, b);

        System.out.println("length of LCS: " + length);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值